Drag and Drop Files Into Upload Boxes
I work on an RSS reader app chosen Readerrr (editor's note: link removed as site seems dead). I wanted to enrich the feed import experience by making allowing for drag and drop file upload alongside the traditional file input. Sometimes drag and drop is a more comfy mode to select a file, isn't it?

View Demo
Markup
This markup doesn't have annihilation specifically to do with drag and drop. It'due south merely a normal, functional<form>
, albeit with some extra HTML elements for potential states.
<form grade="box" method="post" action="" enctype="multipart/form-data"> <div class="box__input"> <input form="box__file" type="file" name="files[]" id="file" data-multiple-caption="{count} files selected" multiple /> <characterization for="file"><strong>Cull a file</strong><span class="box__dragndrop"> or drag it here</span>.</label> <button form="box__button" blazon="submit">Upload</button> </div> <div class="box__uploading">Uploading…</div> <div class="box__success">Done!</div> <div course="box__error">Error! <span></span>.</div> </form>
We'll hibernate those states until we demand them:
.box__dragndrop, .box__uploading, .box__success, .box__error { brandish: none; }
A piffling caption:
- Regarding states:
.box__uploading
chemical element will be visible during the Ajax procedure of file upload (and the others volition all the same exist subconscious). And so.box__success
or.box__error
will be shown depending on what happens. -
input[type="file"]
andlabel
are the functional parts of the course. I wrote about styling these together in my mail service about customizing file inputs. In that post I also described the purpose of[data-multiple-explanation]
attribute. The input and characterization also serve as an alternative for selecting files in the standard fashion (or the only manner if drag and drop isn't supported). -
.box__dragndrop
will be shown if a browser supports drag and drop file upload functionality.
Feature detection
Nosotros tin't 100% rely on browsers supporting drag and drib. We should provide a fallback solution. And so: feature detection. Drag & drop file upload relies on a number of different JavaScript API's, then we'll demand to check on all of them.
Offset, elevate & drop events themselves. Modernizr is a library you can trust all about feature detection. This test is from there:
var div = document.createElement('div'); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)
Adjacent nosotros demand to check the FormData interface, which is for forming a programmatic object of the selected file(s) so they can be sent to the server via Ajax:
return 'FormData' in window;
Last, we need the DataTransfer object. This ane is a bit catchy because there is no bullet-proof way to find the availability of the object earlier user's first interaction with the drag & drop interface. Not all browsers betrayal the object.
Ideally we'd like to avert UX like…
- "Drag and drop files here!"
- [User drags and drops files]
- "Oops simply kidding drag and drop isn't supported."
The trick here is to check the availability of FileReader API correct when the document loads. The thought backside this is that browsers that support FileReader
support DataTransfer
too:
'FileReader' in window
Combining the code above into cocky-invoking anonymous function…
var isAdvancedUpload = function() { var div = document.createElement('div'); return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; }();
… will enable united states of america to make an effective feature support detection:
if (isAdvancedUpload) { // ... }
With this working characteristic detection, now we can let the users know they can elevate & drop their files into our form (or not). We can style the form past calculation a class to it in the case of back up:
var $course = $('.box'); if (isAdvancedUpload) { $form.addClass('has-advanced-upload'); }
.box.has-advanced-upload { background-color: white; outline: 2px dashed black; outline-offset: -10px; } .box.has-advanced-upload .box__dragndrop { display: inline; }

No problems at all if drag & drib file upload is non supported. Wsers will be able to upload files via adept ol' input[blazon="file"]
!

Note on browser support: Microsoft Edge has a bug which stops drag and drop from working. It sounds similar they are aware of information technology and hope to set up information technology. (Update: link to bug removed every bit the link stopped working. Now that Edge is Chromium, presumably, it'southward not a trouble anymore.)
Drag 'north' Drop
Here we go, here's the good stuff.
This part deals with calculation and removing classes to the form on the different states like when the user is dragging a file over the form. Then, catching those files when they are dropped.
if (isAdvancedUpload) { var droppedFiles = false; $class.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) { east.preventDefault(); e.stopPropagation(); }) .on('dragover dragenter', function() { $class.addClass('is-dragover'); }) .on('dragleave dragend drop', function() { $course.removeClass('is-dragover'); }) .on('drib', function(e) { droppedFiles = e.originalEvent.dataTransfer.files; }); }
-
e.preventDefault()
andeastward.stopPropagation()
prevent any unwanted behaviors for the assigned events across browsers. -
e.originalEvent.dataTransfer.files
returns the listing of files that were dropped. Soon you will see how to use the data for sending these files to the server.
Calculation and removing .is-dragover
when necessary enables u.s.a. to visually indicate when it is safe for a user to drop the files:
.box.is-dragover { background-color: grey; }

Selecting Files In a Traditional Way
Sometimes dragging & dropping files is not very comfortable way for selecting files for upload. Particularly when a user is in front of a small screen size computer. Therefore information technology would be nice to permit users choose the method they prefer. The file input and label are hither to allow this. Styling them both in the style I've described allows us to keep the UI consistant:

Ajax Upload
There is no cross-browser fashion to upload dragged & dropped files without Ajax. Some browsers (IE and Firefox) do not allow setting the value of a file input, which and then could be submitted to server in a usual way.
This won't work:
$form.find('input[type="file"]').prop('files', droppedFiles);
Instead, we'll utilize Ajax when the form is submitted:
$grade.on('submit', office(e) { if ($class.hasClass('is-uploading')) return false; $grade.addClass('is-uploading').removeClass('is-error'); if (isAdvancedUpload) { // ajax for modern browsers } else { // ajax for legacy browsers } });
The .is-uploading
class does double duty: it prevents the course from being submitted repeatedly (return false
) and helps to bespeak to a user that the submission is in progress:
.box.is-uploading .box__input { visibility: none; } .box.is-uploading .box__uploading { display: block; }
Ajax for modern browsers
If this was a class without a file upload, we wouldn't need to have two different Ajax techniques. Unfortunately, file uploading via XMLHttpRequest
on IE 9 and below is not supported.
To distinguish which Ajax method will work, we can use our existing isAdvancedUpload
exam, because the browsers which support the stuff I wrote before, also support file uploading via XMLHttpRequest. Here's code that works on IE ten+:
if (isAdvancedUpload) { east.preventDefault(); var ajaxData = new FormData($grade.get(0)); if (droppedFiles) { $.each( droppedFiles, office(i, file) { ajaxData.append( $input.attr('name'), file ); }); } $.ajax({ url: $form.attr('action'), type: $form.attr('method'), data: ajaxData, dataType: 'json', cache: false, contentType: faux, processData: false, complete: function() { $form.removeClass('is-uploading'); }, success: role(information) { $form.addClass( data.success == true ? 'is-success' : 'is-fault' ); if (!data.success) $errorMsg.text(data.error); }, fault: function() { // Log the error, show an alert, any works for you lot } }); }
-
FormData($class.get(0))
collects information from all the class inputs - The
$.each()
loop runs through the dragged & dropped files.ajaxData.append()
adds them to the data stack which will be submitted via Ajax -
data.success
anddata.error
are a JSON format respond which will be returned from the server. Hither'due south what that would be like in PHP:
<?php // ... dice(json_encode([ 'success'=> $is_success, 'fault'=> $error_msg])); ?>
Ajax for legacy browsers
This is essentially for IE ix-. We do not need to collect the dragged & dropped files considering in this case (isAdvancedUpload = false
), the browser does non back up drag & drop file upload and the class relies merely on the input[blazon="file"]
.
Strangely enough, targeting the course on a dynamically inserted iframe does the fob:
if (isAdvancedUpload) { // ... } else { var iframeName = 'uploadiframe' + new Date().getTime(); $iframe = $('<iframe proper noun="' + iframeName + '" style="display: none;"></iframe>'); $('body').append($iframe); $form.attr('target', iframeName); $iframe.ane('load', role() { var data = JSON.parse($iframe.contents().find('trunk' ).text()); $course .removeClass('is-uploading') .addClass(information.success == true ? 'is-success' : 'is-error') .removeAttr('target'); if (!data.success) $errorMsg.text(data.fault); $form.removeAttr('target'); $iframe.remove(); }); }
Automatic Submission
If you take a elementary form with but a drag & drop surface area or file input, information technology may be a user convenience to avert requiring them to press the button. Instead, you can automatically submit the class on file drib/select by triggering the submit
event:
// ... .on('drop', function(e) { // when drag & drib is supported droppedFiles = e.originalEvent.dataTransfer.files; $form.trigger('submit'); }); // ... $input.on('change', office(e) { // when drag & drop is Non supported $form.trigger('submit'); });
If drag & drib area is visually well-designed (information technology's obvious to the user what to do), yous might consider hiding the submit button (less UI can be good). But be careful when hiding a command similar that. The button should be visible and functional if for some reason JavaScript is not available (progressive enhancement!). Adding a .no-js
form name to and removing information technology with JavaScript will practise the trick:
<html class="no-js"> <caput> <!-- remove this if you lot use Modernizr --> <script>(office(due east,t,n){var r=east.querySelectorAll("html")[0];r.className=r.className.supercede(/(^|\s)no-js(\southward|$)/,"$1js$two")})(certificate,window,0);</script> </head> </html>
.box__button { brandish: none; } .no-js .box__button { display: block; }
Displaying the Selected Files
If y'all're not going to do auto-submission in that location should be an indication to the user if they take selected files successfully:
var $input = $form.find('input[type="file"]'), $label = $form.find('label'), showFiles = function(files) { $characterization.text(files.length > 1 ? ($input.attr('data-multiple-explanation') || '').replace( '{count}', files.length ) : files[ 0 ].proper noun); }; // ... .on('driblet', office(eastward) { droppedFiles = e.originalEvent.dataTransfer.files; // the files that were dropped showFiles( droppedFiles ); }); //... $input.on('change', part(e) { showFiles(east.target.files); });


When JavaScript Is Not Available
Progressive enhancement is nigh the thought that a user should be able to consummate the primary tasks on a website no matter what. File uploading is no exception. If for some reason JavaScript is non available, the interface will look like this:

The page will refresh on grade submission. Our JavaScript for indicating the result of submission is useless. That means we have to rely on server-side solution. Here'southward how it looks and works in the demo folio:
<?php $upload_success = null; $upload_error = ''; if (!empty($_FILES['files'])) { /* the code for file upload; $upload_success – becomes "true" or "simulated" if upload was unsuccessful; $upload_error – an error bulletin of if upload was unsuccessful; */ } ?>
And some adjustments for the markup:
<form class="box" method="post" action="" enctype="multipart/form-data"> <?php if ($upload_success === null): ?> <div course="box__input"> <!-- ... --> </div> <?php endif; ?> <!-- ... --> <div form="box__success"<?php if( $upload_success === truthful ): ?> style="display: cake;"<?php endif; ?>>Done!</div> <div form="box__error"<?php if( $upload_success === false ): ?> style="display: block;"<?php endif; ?>>Error! <bridge><?=$upload_error?></span>.</div> </form>
That's information technology! This already-long article could have been even longer, merely I recall this will get you going with a responsible drag and drop file upload feature on your ain projects.
Bank check out the demo for more (view source to meet the no-jQuery-dependency JavaScript):
View Demo
Source: https://css-tricks.com/drag-and-drop-file-uploading/
Postar um comentário for "Drag and Drop Files Into Upload Boxes"