Mozilla

Drag and Drop Articles

Sort by:

View:

  1. Interview with Sebastian Dorn, winner of the Drag and Drop Dev Derby

    Sebastian DornSebastian Dorn won the Drag and Drop Dev Derby with Pete’s Adventure, his wonderful web-based interactive story. Recently, I had the chance to learn more about Seba: his work, his ambitions, and his thoughts on the future of web development.

    The interview

    How did you become interested in web development?

    I think it was around the time I was in middle school. My father read an
    IT magazine and since I was at least a little bit interested, I flicked
    through it as well. There was a series in it about building web sites
    and I thought “I want to try that, building my own site”.

    So I built my first frames-using, table-layouted, GIF-plastered web
    sites–every atrocity you can imagine and some more–using HTML and
    CSS, but without knowing that something like CSS classes existed. Some
    time later I found a free host and put my “Hello, this is me” site
    online. Some years later I became interested in blogging, so I started
    learning PHP and MySQL to write my own CMS.

    Can you tell us a little about how Pete’s Adventure works?

    My goal was to show some other aspect of Drag&Drop in each level:
    Reading meta data like the file size from a dropped file, displaying a
    dropped image or dragging an HTML element from inside the page around.
    There isn’t really anything special in the code. Each level has its own
    JS file with functions to prepare the stage by adding HTML and event
    listeners.

    What was your biggest challenge in developing Pete’s Adventure?

    Not really anything that had to do with coding. At first, I wanted to
    use better drawings. But some horribly misshapen Petes later I gave up
    on that and went ahead with the pixelated look you can see now.

    Then there is the sound and music. I probably sat two hours at the piano
    keyboard, trying to come up with melodies which could be easily looped.
    This was the first time since the recorder lessons in middle school that
    I tried to compose.

    Ah, well, I got a little… agitated while trying to get the drop part of
    Drag&Drop to work for the level where you drag the slimey note to Pete.
    It only works in Firefox when you give the dragged element some transfer
    data, for example an empty string.

    What makes the web an exciting platform for you?

    How easy it is to create and share. Even without a server backend you
    can build exciting demos in HTML/CSS/JS and then just upload it
    somewhere, toss a friend the link and they can see it. To view it, other
    people only need an up-to-date browser–no plugins, no worrying about
    OS compatibility.

    What new web technologies are you most excited about?

    Basically everything that helps making plugins obsolete.

    I wonder if there will be more 3D in-browser rendering with WebGL in the
    future. Animated, interactive films? Games? CAD software?

    Firefox OS and building apps only with JavaScript sounds interesting,
    too. I’m not really that much into mobile development at the moment, but
    I’m interested in how that will develop. Will it become a really good
    alternative to iOS/Android? Or will it end as obscure toy for enthusiasts?

    If you could change one thing about the web, what would it be?

    Making the Internet immune to large scale blocking and censoring. No
    government should be able to cut off the communication channels of its
    people.

    On a less political note: I would be very pleased to see the same audio,
    video and image formats supported in every browser. Finding out that
    WebKit doesn’t support APNG was as a surprise for me.

    Do you have any advice for other ambitious web developers?

    Learning a new language or feature thereof works better, if you put some
    motivation behind it. Maybe you can build an useful browser extension
    with it, or some fascinating demo to show off. Make it fun!

    For other great advice I’d like to quote Jake from Adventure Time:
    “Sucking at something is the first step to becoming sorta good at
    something.”

    Further reading

  2. Announcing the winners of the January 2013 Dev Derby!

    This past January, creative web developers from around the world showed us what they could do with drag and drop interaction in the January Dev Derby contest. After looking through the entries, our three new expert judges–Craig Cook, Franck Lecollinet, and Guillaume Lecollinet–decided on four winners and two runners-up.

    Dev Derby

    Not a contestant? There are other reasons to be excited. Most importantly, all of these demos are completely open-source, making them wonderful lessons in the exciting things you can do with drag and drop today.

    The Results

    Winners

    Runners-up

    These contestants were not satisfied just bringing this age-old interaction to the Web. They also made it fun, helpful, musical, and even a little amusing. Congratulations to them and to all of the amazing contestants who pushed the limit of drag and drop interaction.

    Want to get a head start on an upcoming Derby? We are now accepting demos related to mobile web development in general (March), Web Workers (April), and by popular demand, getUserMedia (May). Head over to the Dev Derby to get started.

  3. The shortest image uploader – ever!

    A couple of line of JavaScript. That’s all you need.

    This is a very short Image Uploader, based on imgur.com API. If you want to do more complex stuff (like resize, crop, drawing, colors, …) see my previous post.

    Back-story. I’ve been talking to Imgur.com‘s owner (Hi Alan!). He recently added Drag’n Drop support to his image sharing website. But also, Alan allows Cross-Domain XMLHttpRequest (thank you!). So basically, you can use his API to upload pictures to his website, from your HTML page, with no server side code involved – at all.

    And here is an example of what you can do:

    (see the full working code on github – live version there )

    (also, you’ll need to understand FormData, see here)

    function upload(file) {
    
      // file is from a  tag or from Drag'n Drop
      // Is the file an image?
    
      if (!file || !file.type.match(/image.*/)) return;
    
      // It is!
      // Let's build a FormData object
    
      var fd = new FormData();
      fd.append("image", file); // Append the file
      fd.append("key", "6528448c258cff474ca9701c5bab6927");
      // Get your own key: http://api.imgur.com/
    
      // Create the XHR (Cross-Domain XHR FTW!!!)
      var xhr = new XMLHttpRequest();
      xhr.open("POST", "http://api.imgur.com/2/upload.json"); // Boooom!
      xhr.onload = function() {
        // Big win!
        // The URL of the image is:
        JSON.parse(xhr.responseText).upload.links.imgur_page;
       }
       // Ok, I don't handle the errors. An exercice for the reader.
       // And now, we send the formdata
       xhr.send(fd);
     }
    

    That’s all :)

    Works on Chrome and Firefox 4 (Edit:) and Safari.

  4. How to develop a HTML5 Image Uploader

    HTML5 comes with a set of really awesome APIs. If you combine these APIs with the <canvas> element, you could create a super/modern/awesome Image Uploader. This article shows you how.

    All these tips work well in Firefox 4. I also describe some alternative ways to make sure it works on Webkit-based browsers. Most of these APIs don’t work in IE, but it’s quite easy to use a normal form as a fallback.

    Please let us know if you use one of these technologies in your project!

    Retrieve the images

    Drag and drop

    To upload files, you’ll need an <input type=”file”> element. But you should also allow the user to drag and drop images from the desktop directly to your web page.

    I’ve written a detailed article about implementing drag-and-drop support for your web pages.

    Also, take a look at the Mozilla tutorial on drag-and-drop.

    Multiple input

    Allow the user the select several files to upload at the same time from the File Picker:

    
    

    Again, here is an article I’ve written about multiple file selection.

    Pre-process the files

    Use the File API

    (See the File API documentation for details.)

    From drag-and-drop or from the <input> element, you have a list a files ready to be used:

    // from an input element
    var filesToUpload = input.files;
    // from drag-and-drop
    function onDrop(e) {
      filesToUpload = e.dataTransfer.files;
    }
    

    Make sure these files are actually images:

    if (!file.type.match(/image.*/)) {
      // this file is not an image.
    };
    

    Show a thumbnail/preview

    There are two options here. You can either use a FileReader (from the File API) or use the new createObjectURL() method.

    createObjectURL()

    var img = document.createElement("img");
    img.src = window.URL.createObjectURL(file);
    

    FileReader

    var img = document.createElement("img");
    var reader = new FileReader();
    reader.onload = function(e) {img.src = e.target.result}
    reader.readAsDataURL(file);
    

    Use a canvas

    Once you have the image preview in an <img> element, you can draw this image in a <canvas> element to pre-process the file.

    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    

    Resize the image

    People are used to uploading images straight from their camera. This gives high resolution and extremely heavy (several megabyte) files. Depending on the usage, you may want to resize such images. A super easy trick is to simply have a small canvas (800×600 for example) and to draw the image tag into this canvas. Of course, you’ll have to update the canvas dimensions to keep the ratio of the image.

    var MAX_WIDTH = 800;
    var MAX_HEIGHT = 600;
    var width = img.width;
    var height = img.height;
    
    if (width > height) {
      if (width > MAX_WIDTH) {
        height *= MAX_WIDTH / width;
        width = MAX_WIDTH;
      }
    } else {
      if (height > MAX_HEIGHT) {
        width *= MAX_HEIGHT / height;
        height = MAX_HEIGHT;
      }
    }
    canvas.width = width;
    canvas.height = height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, width, height);
    

    Edit the image

    Now, you have your image in a canvas. Basically, the possibilities are infinite. Let’s say you want to apply a sepia filter:

    var imgData = ctx.createImageData(width, height);
    var data = imgData.data;
    var pixels = ctx.getImageData(0, 0, width, height);
    for (var i = 0, ii = pixels.data.length; i < ii; i += 4) {
        var r = pixels.data[i + 0];
        var g =pixels.data[i + 1];
        var b = this.pixels.data[i + 2];
        data[i + 0] = (r * .393) + (g *.769) + (b * .189);
        data[i + 1] = (r * .349) + (g *.686) + (b * .168)
        data[i + 2] = (r * .272) + (g *.534) + (b * .131)
        data[i + 3] = 255;
    }
    ctx.putImageData(imgData, 0, 0);
    

    Upload with XMLHttpRequest

    Now that you have loaded the images on the client, eventually you want to send them to the server.

    How to send a canvas

    Again, you have two options. You can convert the canvas to a data URL or (in Firefox) create a file from the canvas.

    canvas.toDataURL()

    var dataurl = canvas.toDataURL("image/png");
    

    Create a file from the canvas

    var file = canvas.mozGetAsFile("foo.png");
    

    Atomic upload

    Allow the user to upload just one file or all the files at the same time.

    Show progress of the upload

    Use the upload events to create a progress bar:

    xhr.upload.addEventListener("progress", function(e) {
      if (e.lengthComputable) {
        var percentage = Math.round((e.loaded * 100) / e.total);
        // do something
    }, false);
    

    Use FormData

    You probably don't want to just upload the file (which could be easily done via: xhr.send(file)) but add side information (like a key and a name).

    In that case, you'll need to create a multipart/form-data request via a FormData object. (See Firefox 4: easier JS form handling with FormData.)

    var fd = new FormData();
    fd.append("name", "paul");
    fd.append("image", canvas.mozGetAsFile("foo.png"));
    fd.append("key", "××××××××××××");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://your.api.com/upload.json");
    xhr.send(fd);
    

    Open your API

    Maybe you want to allow other websites to use your service.

    Allow cross-domain requests

    By default, your API is only reachable from a request created from your own domain. If you want to allow people use your API, allow Cross-XHR in your HTTP header:

    Access-Control-Allow-Origin: *
    

    You can also allow just a pre-defined list of domains.

    Read about Cross-Origin Resource Sharing.

    postMessage

    (Thanks to Daniel Goodwin for this tip.)

    Also, listen to messages sent from postMessage. You could allow people to use your API through postMessage:

    document.addEventListener("message", function(e){
        // retrieve parameters from e.data
        var key = e.data.key;
        var name = e.data.name;
        var dataurl = e.data.dataurl;
        // Upload
    }
    // Once the upload is done, you can send a postMessage to the original window, with URL
    

    That's all. If you have any other tips to share, feel free to drop a comment.

    Enjoy ;)

  5. HTML5 adoption stories: box.net and html5 drag and drop

    This is a guest post from Tomas Barreto, a developer who works at box.net. They recently adopted HTML5 drag and drop as a way to share files with other people using new features in Firefox. The included video is a pitch for the feature and service, but shows how easy it is to do simple HTML5-based upload progress even with multiple files. Tomas gives an overview of the relatively simple JavaScript required to do this, and how improvements in Firefox 4 will make things even easier. Also have a quick look at the bottom of the post for links to additional docs and resources.

    At Box.net, we’re always exploring new ways to help users get content quickly and securely onto our cloud content management platform. So when asked, “What feature would make you use Box more?” during the Box Hack Olympics in April, my colleague CJ and I decided to tackle the most intuitive way to upload files: simply dragging them from the desktop into Box.

    We considered technologies ranging from Gears to Firefox plugins, but only HTML5 had sufficient adoption. By using some of the JavaScript APIs defined in the HTML5 standard, CJ and I could create a seamless drag and drop experience for our users on supporting browsers. Furthermore, using an HTML5-based upload feature would allow us to enable users to select multiple files at once, and also display progress on the client without polling. And with HTML5 adoption across the latest versions of three of the top four browsers, we felt confident about building an upload method based on this new technology without the trade-offs of using a third-party plug-in.

    We rolled out the first rev of our drag and drop feature a few weeks ago, and we’re impressed with how quickly it has been adopted. It’s already one of the most popular ways to get files onto Box, and in its first week it surpassed our old AJAX upload method. You can check out our demo video to get a feel for the feature:

    To build this feature, we referenced a handful of online examples that explained how to use Firefox 3 FileReader object and the drag and drop file event support. Our first implementation used this object to load the file into memory and then took advantage of the latest XMLHttpRequest events to track progress on the client.

    var files = event.originalEvent.dataTransfer.files; // drop event
    var reader = new FileReader();
    
    reader.onload = function(event) {
      var file_contents = event.target.result;
      var request = new XMLHttpRequest();
    
      ... // attach event listeners to monitor progress and detect errors
    
      var post_body = '';
    
      .. // build post body
    
      post_body += file_contents;
    
      .. // finish post body
    
      var url = 'http://example.com/file_upload';
    
      var request = new XMLHttpRequest();
    
      request.open("POST",  url, true); // open asynchronous post request
      request.setRequestHeader('content-type', 'multipart/form-data; boundary=""'); // make sure to set a boundary
      request.send(post_body);
    }
    
    reader.readAsBinaryString(files[0]);
    

    This approach worked well because we could use the same server processing code that we previously used for uploads. The main disadvantage here is that the FileReader object reads the entire file into memory, which is not optimal for a general upload use case. Our current HTML5 implementation uses this logic and has forced us to restrict drag and drop uploads to just 25mb. However, thanks to recommendations from the Mozilla team, we’ll be taking an alternative approach for V2 of drag and drop, where the file is read chunks as needed by the request. Here’s how we’re going to do it:

    var files = event.originalEvent.dataTransfer.files; // drop event
    var url = 'http://example.com/file_upload';
    
    var request = new XMLHttpRequest();
    request.open("POST",  url, true); // open asynchronous post request
    request.send(files[0]);
    

    Since this approach is not formatted as a multipart form-data, it will require some adjustments on our back-end to support receiving file uploads in this way. However, it’s definitely worth the trade-off since we’ll get all the benefits of the previous method and we don’t need special file size restrictions. In the future, we’ll consider using yet another way to efficiently upload files that is supported in Firefox 4 and uses the traditional multi-part form:

    var files = event.originalEvent.dataTransfer.files; // drop event
    var url = 'http://example.com/file_upload';
    
    var request = new XMLHttpRequest();
    
    var fd = new FormData;
    fd.append("myFile", files[0]);
    
    request.open("POST",  url, true); // open asynchronous post request
    request.send(fd);
    

    We’re already exploring more ways to enrich the Box experience using HTML5. With HTML5, we can build faster, richer and more interactive features with native browser support, and bridge the traditional gap between desktop software and web applications. Here are just a few cool new upload-related features on our roadmap:

    • Pause/Resume uploads using the Blob slice API to split files into chunks (this will be a huge robustness boost, especially for large uploads)
    • Allowing uploads to resume even after the browser closes by caching the file using IndexedDB support (possibly in Firefox 4)

    We’d also like to begin a discussion about supporting the reverse drag and drop use case: dragging files from the browser to the desktop. Based on our users’ enthusiasm around the drag and drop upload feature, we think the reverse functionality would well received. If you are interested in contributing to a specification for this feature, please let us know (html5 [-at$] box.net)!

    Resources:

  6. an HTML5 offline image editor and uploader application

    Many web applications use image uploaders: image hosting websites, blog publishing applications, social networks, among many others. Such uploaders have limitations: you can’t upload more than one file at a time and you can’t edit the image before sending it. A plugin is the usual workaround for uploading more than one image, and image modifications are usually done on the server side, which can make the editing process more cumbersome.

    Firefox 3.6 offers many new Open Web features to web developers, including even more HTML5 support. This post describes how to create a sophisticated image editor and uploader built using Open Web technologies.

    See below for a video of the demo with some background.

    Hosted on hacks, publishes to twitpic

    Our web application uploads pictures to twitpic, an image hosting service for Twitter.

    Note that code for this application is actually hosted on the hacks web server but can still upload to Twitpic. Uploading to Twitpic is possible because they opened up their API to Cross Domain XMLHttpRequests for applications like this. (Thank you twitpic!).

    Web Features

    The demo uses the following features from HTML5 included in Firefox 3.6:

    • HTML5 Drag and Drop: You can drag and drop items inside of the web page and drag images from your desktop directly into the browser.
    • HTML5 localStorage: to store the image data across browser restarts
    • HTML5 Application Cache: This allows you to create applications that can be used when the browser isn’t connected to the Internet. It stores the entire app in the browser and also gives you access to online and offline events so you know when you need to re-sync data with a server.
    • HTML5 Canvas: The HTML5 Canvas element is used through this demo to edit and render images.
    • Cross-Origin Resource Sharing to host an application at one site and publish data to another.

    What’s in the demo?

    See the live demo to try the image uploader for yourself. You will need Firefox 3.6 and a twitter account.

    Here’s a full list of the things that this application can do:

    • You can drag images from your desktop or the web into the application.
    • You can see a preview of each image you want to upload.
    • You can drag previews to the trash to delete an image.
    • Images are automatically made smaller if they are bigger than 600px wide.
    • You can edit any of the images before uploading. This includes being able to rotate, flip, crop or turn an image black and white.
    • If you edit an image it’s saved locally so you can still edit when you’re offline. If you close the tab, restart Firefox or your computer they will be there when you load the page again so you can upload when you’re re-connected.
    • It will upload several files at once and provide feedback as the upload progresses.
    • The HTML5 Offline Application Cache makes the application load very quickly since it’s all stored offline.

    Under the Hood

    Let’s quickly go over all of the technology that we’re using in this application.

    Cross-XMLHttpRequest

    Twitpic was nice enough to open their API to allow XMLHttpRequests from any other domain. This means that you can now use their API from your own website and offer your own image uploader.

    If you’re running a web site with an API you want people to use from other web sites, you can do that with Cross-site HTTP requests. In order for you to support it you will need to add an HTTP header to responses from your web server that says which domains you allow. As an example, here’s how twitpic allows access from all domains:

    Access-Control-Allow-Origin: *
    

    It’s important to note that opening your API does have security implications so you should be careful to understand those issues before blindly opening an API. For more details, see MDC documentation on CORS.

    Drag and Drop

    Drag and Drop is a mechanism with two important features:

    • Dragging files from your Desktop to your web page.
    • Native Drag and Drop inside your web page (not just changing the coordinates of your elements).

    The image uploader uses Drag and Drop to allow the user the add files from the Desktop, to remove files (drag them to the trash) and to insert a new image into a current image.

    For more on Drag and Drop, see previous hacks articles, in particular how to use Drag and Drop in your application.

    Canvas to Edit Images

    Once images have been dragged and dropped into the web page, the image uploader lets you edit them before uploading. This is possible because images are actually copied to canvas elements via the File API.

    In this case, the editing process is really basic: rotate, flip, add text, black and white, crop. However, you can imagine offering many other features in your version of the editor (see Pixastic for example, or this inlay feature here).

    Using canvas and the File API also let you resize the image before sending it. Here, every image is converted to a new image (canvas) that is less than 600px.

    localStorage: Save Local Data

    It’s possible to store local data persistently in a web page using localStorage, up to 5Mb of data per domain.

    In the image uploader, localStorage is used to store images and credentials. Since images are actually canvas, you can store them as data URLs:

    var url = canvas.getContext("2d").toDataURL("image/png");
    localStorage.setItem("image1", url);
    

    LocalStorage support means that you can edit an image, close Firefox, switch off your computer, and the edited image will still be there when you restart Firefox.

    Offline

    If you add a manifest file listing all remote files needed to display your web application it will work even when you aren’t connected to the Internet. A nice side effect is that it will also make your application load much faster.

    Here, the html element refers to a manifest file:

    
    

    And the manifest file looks like:

    CACHE MANIFEST
    
    # v2.4
    index.xhtml
    fonts/MarketingScript.ttf
    css/desktop.css
    css/fonts.css
    css/mobile.css
    [...]
    

    You can also catch offline and online events to know if the connection status changes.
    For more information see our last article about offline.

    Conclusion

    Firefox 3.6 allows millions of people to take advantage of modern standards, including HTML5.
    The image uploader described here shows how a web page should really be considered as an application since it interacts with your Desktop and works offline.

    Here are a few tips for writing your next application using Open Web technologies:

    Allow Cross-XMLHttpRequest:
    If it makes sense for your service, allow people to access your API from a different domain, you’ll be amazed at the apps people will come up with.

    Allow multiple input:
    Let people Drag files to your application and use <input type="file" multiple=""> so they can select several files at once. In this demo, we use a multiple input which is visible only in the mobile version, but for accessibility consideration, don’t forget to use it to propose an alternative to Drag’n Drop.

    Use native Drag and Drop:
    Drag and Drop mechanisms are usually simulated (updating coordinates on the mousemove event.) When you can, use the native mechanism.

    Use the File API
    To pre-process a file before even talking to a server.

    Support offline
    Store data and use a manifest to make your application data persistent while offline.

    Use Canvas
    Canvas is the most widely implemented HTML5 element. It works everywhere (even if it has to be simulated), use it!

    Think “Client Side”: HTML5, CSS3 and the new powerful JavaScript engines let you create amazing applications, take advantage of them!

    We look forward to seeing the great new applications you’ll come up with using Open Web technologies!

  7. interactive file uploads with Drag and Drop, FileAPI and XMLHttpRequest

    In previous posts, we showed how to access a file through the input tag or through the Drag and Drop mechanism. In both cases, you can use XMLHttpRequest to upload the files and follow the upload progress.

    Demo

    If you’re running the latest beta of Firefox 3.6, check out our file upload demo.

    Uploading

    XMLHttpRequest will send a given file to the server as a binary array, so you first need to read the content of the file as a binary string, using the File API. Because both Drag and Drop and the input tag allow you to handle multiple files at once, you’ll need to create as many requests as there are files.

    var reader = new FileReader();
    reader.onload = function(e) {
      var bin = e.target.result;
      // bin is the binaryString
    };
    reader.readAsBinaryString(file);
    

    Once the file is read, send it to the server with XMLHttpRequest:

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "upload.php");
    xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
    xhr.sendAsBinary(bin);
    

    You can choose to be notified when specific events, such as error, success, or abort, occur during the request (see the MDC documentation for more details).

    Following the Upload Progress

    The progress event provides the size of the uploaded portion of the binary content. This allows you to easily compute how much of the file has been uploaded.

    Here’s an example showing the percentage of the file that has been uploaded so far:

    xhr.upload.addEventListener("progress", function(e) {
      if (e.lengthComputable) {
        var percentage = Math.round((e.loaded * 100) / e.total);
        // do something
    }, false);
    
  8. file drag and drop in Firefox 3.6

    In a previous post, we showed you how to upload several files using the input element. In Firefox 3.6, you can let your users drag and drop files directly into your web page, without going through the file picker.

    Demo

    If you’re running the latest Firefox 3.6 beta, check out our interactive demo of drag and drop. It showcases two technologies: the Drag and Drop API and the File API.

    Drag and Drop Events

    To use drag and drop, you first need to tell the browser that a given element can handle dropped objects and will respond to a drop action, using the dragover and drop events.

    You also need to prevent the browser’s default behavior, which is to simply load the dropped object in the browser window.

    dropzone.addEventListener("dragover", function(event) {
      event.preventDefault();
    }, true);
    dropzone.addEventListener("drop", function(event) {
      event.preventDefault();
      // Ready to do something with the dropped object
    }, true);
    

    You may also want to use the dragenter and dragleave events to be notified when a drag session starts or stops.

    Your element is now ready to receive files with the drop event.

    Manipulating the Files

    On the drop event, you can access the files with the files property of the DataTransfer object:

    dropzone.addEventListener("drop", function(event) {
      event.preventDefault();
      // Ready to do something with the dropped object
      var allTheFiles = event.dataTransfer.files;
      alert("You've just dropped " + allTheFiles.length + " files");
    }, true);
    

    Once you’ve accessed the files, the File API lets you do much more, like parsing files as a binary array, or displaying a preview of an image by reading the file as a DataURL.

    Of course, you can still drag and drop data other than files (e.g. text, URLs, remote images …) using the drag and drop API.

  9. W3C FileAPI in Firefox 3.6

    Often, web applications will prompt the user to select a file, typically to upload to a server. Unless the web application makes use of a plugin, file selection occurs through an HTML input element, of the sort <input type="file"/>. Firefox 3.6 now supports much of the W3C File API, which specifies the ability to asynchronously read the selected file into memory, and perform operations on the file data within the web application (for example, to display a thumbnail preview of an image, before it is uploaded, or to look for ID3 tags within an MP3 file, or to look for EXIF data in JPEG files, all on the client side). This is a new API, and replaces the file API that was introduced in Firefox 3.

    It is important to note that even before the advent of the W3C File API draft (which only became a Working Draft in November 2009), Firefox 3 and later provide the ability to read files into memory synchronously but that capability should be considered deprecated in favor of the new implementation in Firefox 3.6 of the asynchronous File API. The deprecated API allowed you synchronously access a file:

    // After obtaining a handle to a file
    // access the file data
    var dataURL = file.getAsDataURL();
    img.src = dataURL;

    While Firefox 3.6 will continue to support code usage of the sort above, it should be considered deprecated since it reads files synchronously on the main thread. For large files, this could result in blocking on the result of the read, which isn’t desirable. Moreover, the file object itself provides a method to read from it, rather than having a separate reader object. These considerations informed the technical direction of the new File API in Firefox 3.6 (and the direction of the specification). The rest of this article is about the newly introduced File API.

    Accessing file selections

    Firefox 3.6 supports multiple file selections on an input element, and returns all the files selected using the FileList interface. Previous versions of Firefox only supported one selection of a file using the input element. Additionally, the FileList interface is also exposed to the HTML5 Drag and Drop API as a property of the DataTransfer interface. Users can drag and drop multiple files to a drop target within a web page as well.

    The following HTML spawns the standard file picker, with which you can select multiple files:

    Note that if you don’t use the multiple attribute, you only enable single file selection.

    You can work with all the selected files obtained either through the file picker (using the input element) or through the DataTransfer object by iterating through the FileList:

    var files = document.getElementById("inputFiles").files;
    
    // or, for a drag event e:
    // var dt = e.dataTransfer; var files = dt.files
    
    for (var i = 0; i < files.length; i++) {
      var file = files[i];
      handleFile(file);
    
    }

    Properties of files

    Once you obtain a reference to an individually selected file from a FileList, you get a File object, which has name, type, and size properties. Continuing with the code snippet above:

    function handleFile(file) {
        // RegExp for JPEG mime type
        var imageType = /image/jpeg/;
    
        // Check if match
        if (!file.type.match(imageType)) {
            return false;
        }
       // Check if the picture exceeds set limit
       if(file.size > maxSize) {
          alert("Choose a smaller photo!");
          return false;
       }
      // Add file name to page
      var picData = document.createTextNode(file.name);
      dataGrid.appendChild(picData);
      return true;
    }

    The size attribute is the file's size, in bytes. The name attribute is the file's name, without path information. The type attribute is an ASCII-encoded string in lower case representing the media type of the file, expressed as an RFC2046 MIME type. The type attribute in particular is useful in sniffing file type, as in the example above, where the script determines if the file in question is a JPEG file. If Firefox 3.6 cannot determine the file's type, it will return the empty string.

    Reading Files

    Firefox 3.6 and beyond support the FileReader object to read file data asynchronously into memory, using event callbacks to mark progress. The object is instantiated in the standard way:

    var binaryReader = new FileReader();

    Event handler attributes are used to work with the result of the file read operation. For very large files, it is possible to watch for progress events as the file is being read into memory (using the onprogress event handler attribute to set the event handler function). This is useful in scenarios where the drives in question may not be local to the hardware, or if the file in question is particularly big.

    The FileReader object supports three methods to read files into memory. Each allows programmatic access to the files data in a different format, though in practice only one read method should be called on a given FileReader object:

    • filereader.readAsBinaryString(file); will asynchronously return a binary string with each byte represented by an integer in the range [0..255]. This is useful for binary manipulations of a file's data, for example to look for ID3 tags in an MP3 file, or to look for EXIF data in a JPEG image.
    • filereader.readAsText(file, encoding); will asynchronously return a string in the format solicited by the encoding parameter (for example encoding = "UTF-8"). This is useful for working with a text file, for example to parse an XML file.
    • filereader.readAsDataURL(file); will asynchronously return a Data URL. Firefox 3.6 allows large URLs, and so this feature is particularly useful when a URL could help display media content in a web page, for example for image data, video data, or audio data.

    An example helps tie this all together:

    if (files.length > 0) {
        if (!handleFile(files[0])) {
            invalid.style.visibility="visible";
            invalid.msg = "Select a JPEG Image";
         }
    }
    
    var binaryReader = new FileReader();
    binaryReader.onload = function(){
       var exif = findEXIFInJPG(binaryReader.result);
       if (!exif) {
          // ...set up conditions for lack of data
       }
       else {
        // ...write out exif data
       }
    
    binaryReader.onprogress = updateProgress;
    binaryReader.onerror = errorHandler;
    
    binaryReader.readAsBinaryString(file);
    
    function updateProgress(evt){
       // use lengthComputable, loaded, and total on ProgressEvent
       if (evt.lengthComputable) {
              var loaded = (evt.loaded / evt.total);
              if (loaded < 1) {
                // update progress meter
                progMeter.style.width = (loaded * 200) + "px";
              }
       }
    }
    
    function errorHandler(evt) {
      if(evt.target.error.code == evt.target.error.NOT_FOUND_ERR) {
       alert("File Not Found!");
      }
    }

    In order to work with binary data, the use of the charCodeAt function exposed on strings will be particularly useful. For instance, an utility of the sort:

    function getByteAt(file, idx) {
        return file.charCodeAt(idx);
    }

    allows extraction of the Unicode value of the character at the given index.

    An example of similar code in action in Firefox 3.6, including use of the readAsDataURL method to render an image, as well as binary analysis of a JPEG for EXIF detection (using the readAsBinaryString method), can be found in Paul Rouget's great demo of the File API..

    A word on the specification

    The existence of a W3C public working draft of the File API holds the promise of other browsers implementing it shortly. Firefox 3.6's implementation is fairly complete, but is missing some of the technology mentioned in the specification. Notably, the urn feature on the File object isn't yet implemented, and neither is the ability to extract byte-ranges of files using the slice method. A synchronous way to read files isn't yet implemented as part of Worker Threads. These features will come in future versions of Firefox.

    References

  10. font_dragr: a drag and drop preview tool for fonts

    This demo is from our good friend Ryan Seddon who came up with a demo that seems deeply appropriate for this week, given our focus on the future of fonts on the web.

    If you’ve ever been editing a page and wanted to know what a particular font looked like without having to upload files to a web server and update CSS (so tedious, that!) then this demo is for you.

    He’s come up with a demo that shows what’s possible when you’ve got downloadable fonts, drag and drop and editable content. (If you want to know more about drag and drop we suggest you read his excellent overview of using drag and drop to do file uploading.)

    From Ryan’s description:

    Font dragr is an experimental web app that uses HTML5 & CSS3 to create a useful standalone web based application for testing custom fonts, once you visit it for the first time you don’t need to be online to use it after the initial visit. It allows you, in Firefox 3.6+, to drag and drop font files from your file system into the drop area. The browser will then create a data URL encoded copy to use in the page and render the content in the dropped font.

    You can either read the full description, which contains a lot of useful technical information about how the demo works, or you can view the demo below. Either way, it’s nice to see the excellent HTML5 support in Firefox 3.6 coming together with everything else we’ve added to bring a lot of new capabilities to web developers and users.

    Thanks, Ryan!