Friday, March 29, 2024

Drag Files Into the Browser From the Desktop using jQuery Event Binding

Drag Files Into the Browser From the Desktop using jQuery Event Binding

A little while ago, I wrote how to use HTML5 Drag & Drop and JavaScript FileReader to accept image files from the Desktop and display them in the browser in an article entitled Drag Files Into the Browser From the Desktop with HTML5. I can see from user comments that the article would have perhaps benefitted from a demo. If only I had known about sites like JSFiddle and Codepen back then! Eventually, a few readers took it upon themselves to post a demo (thanks Dhanck and JacobEdmondKerr!). Looking back at that code now, I can see how much effort went into making it cross-browser compatible. All that work could have been greatly minimized by employing a good JS library. It can take on the burden of managing browser inconsistencies so that we can focus on the problem at hand. With that in mind, today’s article will present a jQuery-enhanced version of the code from Drag Files Into the Browser From the Desktop with HTML5. I think that you’ll be pleasantly surprised at how much lighter the script is now, and to see that there is a demo this time around!

The Setup

This demo presents a table element that accepts files from the desktop or Windows Explorer. I originally tried a DIV but I didn’t like that it didn’t automatically expand vertically as more files were dragged over. A table does this automatically. The second row contains an empty unordered list to which we will append dropped files:

<table id="holder">
  <tr>
    <td>Drop files here</td>
  </tr>
  <tr>
    <td><ul id="fileList"></ul></td>
  </tr>
</table>

The Script Explained

Binding Events jQuery-style

jQuery’s excellent on() function makes it easy to attach multiple events and handlers to the same element or attach multiple handlers to the same event. In the first case, you just wrap the element in the jQuery $() selector and then invoke on() as an instance method. It accepts an object where each name/value pair represents an event (or event name) and event handler. To bind multiple handlers to the same event, you can separate the event names by a space. All of the drag & drop related events have to be short-circuited in order to prevent the browser from trying to open the files.

$('#holder').on({
    'dragover dragenter': function(e) {
        e.preventDefault();
        e.stopPropagation();
    },
    'drop': function(e) {
      //our code will go here
    }
});

The Drop Event Handler

The drop event handler contains a relatively small amount of dense functional-style code – also courtesy of jQuery. jQuery passes the event object to our handler, but it is not the native browser event. jQuery exposes it via the originalEvent property, so that we can still access the underlying native event, if need be. The DragEvent is a particular type of event that possesses a dataTransfer attribute. It in turn holds a reference to the files object, which contains a list of all the local files available from the data transfer. Since the files are stored within an array, we can check its length to see whether or not there are files to process. The array could have a zero length because the DataTransfer object does support other object types.

Once again, the event is prevented from bubbling before we process the files.

'drop': function(e) {
    var dataTransfer =  e.originalEvent.dataTransfer;
    if( dataTransfer && dataTransfer.files.length) {
        e.preventDefault();
        e.stopPropagation();
        //more code here...
    }
}

The next bit of code has changed quite a bit as a result of the jQuery overhaul. For starters, $.each() is employed to iterate over the files. Inside the passed function, we instantiate a new FileReader object. The onload event is set to our handler, just like before, except that this time, I replaced the closure with a call to jQuery’s proxy() function. It conveniently binds some variables to the onload handler so that they are accessible to it. You never want to depend on variables and objects that reside in an outer scope to the handler as these can change by the time the handler executes. Finally, we invoke the FileReader instance’s readAsDataURL() method, passing in the current file.

I also made use of the file’s type property to display a thumbnail of the image if applicable. The list item and image are appended in reverse order so that the last file dropped would appear at the top of the list. This is easy to achieve using the jQuery prepend() function.

if( dataTransfer && dataTransfer.files.length) {
    e.preventDefault();
    e.stopPropagation();
    $.each( dataTransfer.files, function(i, file) {
      var reader = new FileReader();
      reader.onload = $.proxy(function(file, $fileList, event) {
        var img = file.type.match('image.*') ? "<img src='" + event.target.result + "' /> " : "";
        $fileList.prepend( $("<li>").append( img + file.name ) );
      }, this, file, $("#fileList"));
      reader.readAsDataURL(file);
    });
}

The Demo

As promised, there is a full demo up on Codepen.

Conclusion

jQuery has been around a long time now, but it’s still my favorite JS library because once you are familiar with its idioms it really does take out the sting of coding for multiple browsers. If anyone wants to fork the demo and add functionality such as file upload capability, error handling, and more detailed progress reports, be sure to let everyone know in the comments!

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured