Html 5 Drag and Drop File Upload
I piece of work on an RSS reader app chosen Readerrr (editor's note: link removed every bit site seems expressionless). I wanted to enrich the feed import experience by making allowing for drag and driblet file upload aslope the traditional file input. Sometimes drag and driblet is a more comfortable way to select a file, isn't it?
View Demo
Markup
This markup doesn't have anything specifically to do with drag and drib. It's just a normal, functional<grade>, albeit with some extra HTML elements for potential states.
<course class="box" method="mail" activity="" enctype="multipart/form-data"> <div class="box__input"> <input class="box__file" type="file" proper noun="files[]" id="file" data-multiple-caption="{count} files selected" multiple /> <characterization for="file"><strong>Choose a file</strong><bridge class="box__dragndrop"> or drag it here</span>.</label> <push class="box__button" blazon="submit">Upload</button> </div> <div class="box__uploading">Uploading…</div> <div grade="box__success">Done!</div> <div class="box__error">Error! <span></span>.</div> </form> We'll hide those states until we need them:
.box__dragndrop, .box__uploading, .box__success, .box__error { brandish: none; } A piddling explanation:
- Regarding states:
.box__uploadingchemical element will be visible during the Ajax process of file upload (and the others will nevertheless be hidden). And then.box__successor.box__errorvolition exist shown depending on what happens. -
input[type="file"]andlabelare the functional parts of the form. I wrote about styling these together in my mail about customizing file inputs. In that post I also described the purpose of[information-multiple-caption]attribute. The input and label likewise serve as an alternative for selecting files in the standard way (or the only way if elevate and drop isn't supported). -
.box__dragndropwill exist shown if a browser supports drag and drop file upload functionality.
Feature detection
Nosotros tin can't 100% rely on browsers supporting elevate and drop. We should provide a fallback solution. And and then: feature detection. Elevate & driblet file upload relies on a number of different JavaScript API's, then we'll need to bank check on all of them.
First, elevate & drop events themselves. Modernizr is a library you tin can trust all about characteristic detection. This test is from there:
var div = document.createElement('div'); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div) Next we need to check the FormData interface, which is for forming a programmatic object of the selected file(south) and so they tin can be sent to the server via Ajax:
return 'FormData' in window; Concluding, nosotros need the DataTransfer object. This one is a bit tricky considering at that place is no bullet-proof style to detect the availability of the object earlier user'due south outset interaction with the drag & driblet interface. Not all browsers betrayal the object.
Ideally nosotros'd like to avoid UX like…
- "Drag and drop files hither!"
- [User drags and drops files]
- "Oops just kidding drag and drib isn't supported."
The play tricks here is to cheque the availability of FileReader API right when the document loads. The thought behind this is that browsers that support FileReader support DataTransfer too:
'FileReader' in window Combining the code to a higher place into cocky-invoking anonymous office…
var isAdvancedUpload = function() { var div = document.createElement('div'); render (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; }(); … will enable us to brand an effective characteristic support detection:
if (isAdvancedUpload) { // ... } With this working characteristic detection, now we tin can let the users know they can drag & driblet their files into our form (or non). Nosotros can style the form by calculation a class to information technology in the case of support:
var $form = $('.box'); if (isAdvancedUpload) { $form.addClass('has-advanced-upload'); } .box.has-advanced-upload { background-color: white; outline: 2px dashed black; outline-offset: -10px; } .box.has-avant-garde-upload .box__dragndrop { display: inline; }
No bug at all if drag & drop file upload is not supported. Wsers will be able to upload files via good ol' input[type="file"]!
Annotation on browser support: Microsoft Border has a problems which stops elevate and drop from working. Information technology sounds like they are enlightened of it and promise to fix it. (Update: link to bug removed every bit the link stopped working. Now that Border is Chromium, presumably, information technology'southward not a problem anymore.)
Drag 'n' Drop
Here nosotros go, here'southward the skillful stuff.
This office deals with adding and removing classes to the form on the different states similar when the user is dragging a file over the class. And then, communicable those files when they are dropped.
if (isAdvancedUpload) { var droppedFiles = false; $form.on('drag dragstart dragend dragover dragenter dragleave drop', part(e) { eastward.preventDefault(); east.stopPropagation(); }) .on('dragover dragenter', function() { $grade.addClass('is-dragover'); }) .on('dragleave dragend drib', function() { $form.removeClass('is-dragover'); }) .on('driblet', role(eastward) { droppedFiles = eastward.originalEvent.dataTransfer.files; }); } -
e.preventDefault()ande.stopPropagation()forbid any unwanted behaviors for the assigned events across browsers. -
e.originalEvent.dataTransfer.filesreturns the listing of files that were dropped. Soon you volition run across how to use the data for sending these files to the server.
Adding and removing .is-dragover when necessary enables the states to visually indicate when it is rubber for a user to drop the files:
.box.is-dragover { background-color: grayness; }
Selecting Files In a Traditional Way
Sometimes dragging & dropping files is not very comfy way for selecting files for upload. Especially when a user is in front of a small screen size reckoner. Therefore information technology would be prissy to let users choose the method they adopt. The file input and label are here to allow this. Styling them both in the style I've described allows us to proceed the UI consistant:
Ajax Upload
There is no cross-browser fashion to upload dragged & dropped files without Ajax. Some browsers (IE and Firefox) practice not allow setting the value of a file input, which so could be submitted to server in a usual way.
This won't piece of work:
$form.find('input[blazon="file"]').prop('files', droppedFiles); Instead, nosotros'll employ Ajax when the form is submitted:
$course.on('submit', function(e) { if ($form.hasClass('is-uploading')) return faux; $grade.addClass('is-uploading').removeClass('is-error'); if (isAdvancedUpload) { // ajax for modernistic browsers } else { // ajax for legacy browsers } }); The .is-uploading class does double duty: information technology prevents the form from being submitted repeatedly (return false) and helps to indicate 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 mod browsers
If this was a course without a file upload, nosotros wouldn't need to have 2 different Ajax techniques. Unfortunately, file uploading via XMLHttpRequest on IE 9 and below is not supported.
To distinguish which Ajax method volition work, we tin can employ our existing isAdvancedUpload test, because the browsers which back up the stuff I wrote before, likewise support file uploading via XMLHttpRequest. Hither's code that works on IE ten+:
if (isAdvancedUpload) { e.preventDefault(); var ajaxData = new FormData($form.get(0)); if (droppedFiles) { $.each( droppedFiles, function(i, file) { ajaxData.suspend( $input.attr('name'), file ); }); } $.ajax({ url: $form.attr('action'), type: $form.attr('method'), data: ajaxData, dataType: 'json', cache: simulated, contentType: false, processData: false, complete: function() { $form.removeClass('is-uploading'); }, success: function(data) { $class.addClass( information.success == truthful ? 'is-success' : 'is-error' ); if (!data.success) $errorMsg.text(data.error); }, error: part() { // Log the fault, bear witness an alert, any works for you } }); } -
FormData($grade.get(0))collects data from all the form inputs - The
$.each()loop runs through the dragged & dropped files.ajaxData.append()adds them to the data stack which will exist submitted via Ajax -
data.successanddata.errorare a JSON format answer which volition be returned from the server. Here'due south what that would exist like in PHP:
<?php // ... die(json_encode([ 'success'=> $is_success, 'error'=> $error_msg])); ?> Ajax for legacy browsers
This is substantially for IE 9-. Nosotros do not need to collect the dragged & dropped files because in this case (isAdvancedUpload = false), the browser does not support drag & drib file upload and the form relies only on the input[blazon="file"].
Strangely enough, targeting the form on a dynamically inserted iframe does the play tricks:
if (isAdvancedUpload) { // ... } else { var iframeName = 'uploadiframe' + new Appointment().getTime(); $iframe = $('<iframe name="' + iframeName + '" style="display: none;"></iframe>'); $('body').append($iframe); $form.attr('target', iframeName); $iframe.one('load', function() { var data = JSON.parse($iframe.contents().find('body' ).text()); $form .removeClass('is-uploading') .addClass(information.success == true ? 'is-success' : 'is-error') .removeAttr('target'); if (!information.success) $errorMsg.text(information.mistake); $class.removeAttr('target'); $iframe.remove(); }); } Automatic Submission
If yous have a unproblematic form with only a drag & drop expanse or file input, it may be a user convenience to avoid requiring them to printing the button. Instead, you can automatically submit the form on file driblet/select by triggering the submit issue:
// ... .on('drop', function(e) { // when drag & driblet is supported droppedFiles = eastward.originalEvent.dataTransfer.files; $grade.trigger('submit'); }); // ... $input.on('change', function(east) { // when elevate & drop is Not supported $form.trigger('submit'); }); If drag & drop expanse is visually well-designed (it'south obvious to the user what to practise), y'all might consider hiding the submit push button (less UI can be good). But exist careful when hiding a control like that. The button should be visible and functional if for some reason JavaScript is not bachelor (progressive enhancement!). Calculation a .no-js class name to and removing it with JavaScript will do the trick:
<html class="no-js"> <head> <!-- remove this if you utilize Modernizr --> <script>(function(eastward,t,n){var r=e.querySelectorAll("html")[0];r.className=r.className.supervene upon(/(^|\southward)no-js(\s|$)/,"$1js$2")})(document,window,0);</script> </head> </html> .box__button { display: none; } .no-js .box__button { display: cake; } Displaying the Selected Files
If you're non going to do automobile-submission there should be an indication to the user if they take selected files successfully:
var $input = $form.find('input[blazon="file"]'), $label = $grade.find('label'), showFiles = function(files) { $label.text(files.length > 1 ? ($input.attr('information-multiple-caption') || '').supersede( '{count}', files.length ) : files[ 0 ].name); }; // ... .on('drib', function(e) { droppedFiles = due east.originalEvent.dataTransfer.files; // the files that were dropped showFiles( droppedFiles ); }); //... $input.on('alter', function(e) { showFiles(e.target.files); });
When JavaScript Is Non Bachelor
Progressive enhancement is about the idea that a user should exist able to complete the primary tasks on a website no thing what. File uploading is no exception. If for some reason JavaScript is not bachelor, the interface will wait similar this:
The page will refresh on form submission. Our JavaScript for indicating the result of submission is useless. That means we take to rely on server-side solution. Here'south how it looks and works in the demo page:
<?php $upload_success = nada; $upload_error = ''; if (!empty($_FILES['files'])) { /* the code for file upload; $upload_success – becomes "truthful" or "fake" 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/grade-information"> <?php if ($upload_success === null): ?> <div grade="box__input"> <!-- ... --> </div> <?php endif; ?> <!-- ... --> <div class="box__success"<?php if( $upload_success === truthful ): ?> mode="display: block;"<?php endif; ?>>Done!</div> <div class="box__error"<?php if( $upload_success === false ): ?> fashion="brandish: cake;"<?php endif; ?>>Mistake! <span><?=$upload_error?></span>.</div> </form> That's it! This already-long article could have been even longer, but I call back this will get y'all going with a responsible drag and drop file upload feature on your own projects.
Check out the demo for more than (view source to see the no-jQuery-dependency JavaScript):
View Demo
Source: https://css-tricks.com/drag-and-drop-file-uploading/
0 Response to "Html 5 Drag and Drop File Upload"
Post a Comment