Click or Drag to Upload How to

I work on an RSS reader app called 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 drib is a more comfortable fashion to select a file, isn't information technology?

View Demo

Markup

This markup doesn't have anything specifically to do with elevate and drop. It's just a normal, functional<form>, albeit with some extra HTML elements for potential states.

          <form course="box" method="post" action="" enctype="multipart/class-data">   <div class="box__input">     <input class="box__file" blazon="file" name="files[]" id="file" data-multiple-caption="{count} files selected" multiple />     <label for="file"><strong>Choose a file</stiff><bridge class="box__dragndrop"> or drag information technology here</span>.</characterization>     <button class="box__button" blazon="submit">Upload</push>   </div>   <div class="box__uploading">Uploading…</div>   <div class="box__success">Washed!</div>   <div form="box__error">Error! <bridge></span>.</div> </form>        

We'll hide those states until we demand them:

          .box__dragndrop, .box__uploading, .box__success, .box__error {   display: none; }        

A little caption:

  • Regarding states: .box__uploading chemical element will be visible during the Ajax process of file upload (and the others will withal exist hidden). And so .box__success or .box__error will be shown depending on what happens.
  • input[type="file"] and label are the functional parts of the grade. I wrote about styling these together in my post near customizing file inputs. In that mail service I also described the purpose of [data-multiple-caption] attribute. The input and characterization likewise serve as an culling for selecting files in the standard way (or the but way if drag and drop isn't supported).
  • .box__dragndrop will be shown if a browser supports elevate and drop file upload functionality.

Feature detection

Nosotros can't 100% rely on browsers supporting drag and drop. We should provide a fallback solution. And so: characteristic detection. Drag & drop file upload relies on a number of dissimilar JavaScript API's, then nosotros'll demand to check on all of them.

First, elevate & drop events themselves. Modernizr is a library you lot can trust all about characteristic detection. This test is from in that location:

          var div = document.createElement('div'); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)        

Next nosotros need 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:

          render 'FormData' in window;        

Terminal, we need the DataTransfer object. This one is a bit tricky because there is no bullet-proof way to detect the availability of the object before user'southward first interaction with the drag & drop interface. Not all browsers expose the object.

Ideally we'd like to avert UX similar…

  • "Drag and drop files here!"
  • [User drags and drops files]
  • "Oops just kidding drag and drop isn't supported."

The trick here is to check the availability of FileReader API correct when the document loads. The idea behind this is that browsers that support FileReader back up DataTransfer besides:

          'FileReader' in window        

Combining the code above into self-invoking anonymous office…

          var isAdvancedUpload = function() {   var div = document.createElement('div');   return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; }();        

… volition enable us to make an effective feature support detection:

          if (isAdvancedUpload) {   // ... }        

With this working feature detection, at present nosotros can let the users know they tin drag & drop their files into our form (or not). We can style the form past adding a grade to it in the case of support:

          var $course = $('.box');  if (isAdvancedUpload) {   $form.addClass('has-advanced-upload'); }        
          .box.has-advanced-upload {   background-colour: white;   outline: 2px dashed black;   outline-offset: -10px; } .box.has-avant-garde-upload .box__dragndrop {   display: inline; }        

No bug at all if drag & drib 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 bug which stops drag and drop from working. It sounds like they are aware of information technology and hope to fix information technology. (Update: link to issues removed as the link stopped working. Now that Edge is Chromium, presumably, it'south not a trouble anymore.)

Drag 'n' Driblet

Here we get, here's the good stuff.

This part deals with adding and removing classes to the course on the different states like when the user is dragging a file over the class. Then, catching those files when they are dropped.

          if (isAdvancedUpload) {    var droppedFiles = false;    $grade.on('drag dragstart dragend dragover dragenter dragleave drop', part(e) {     e.preventDefault();     e.stopPropagation();   })   .on('dragover dragenter', function() {     $form.addClass('is-dragover');   })   .on('dragleave dragend drop', function() {     $form.removeClass('is-dragover');   })   .on('drop', part(e) {     droppedFiles = due east.originalEvent.dataTransfer.files;   });  }        
  • e.preventDefault() and due east.stopPropagation() prevent whatsoever unwanted behaviors for the assigned events across browsers.
  • e.originalEvent.dataTransfer.files returns the listing of files that were dropped. Presently y'all will see how to employ the information for sending these files to the server.

Adding and removing .is-dragover when necessary enables us to visually indicate when it is safe for a user to drib the files:

          .box.is-dragover {   background-colour: greyness; }        

Selecting Files In a Traditional Style

Sometimes dragging & dropping files is not very comfortable fashion for selecting files for upload. Specially when a user is in front end of a small screen size computer. Therefore it would be nice to allow 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 go on the UI consistant:

Ajax Upload

There is no cross-browser way to upload dragged & dropped files without Ajax. Some browsers (IE and Firefox) practice not allow setting the value of a file input, which then could be submitted to server in a usual fashion.

This won't work:

          $class.find('input[type="file"]').prop('files', droppedFiles);        

Instead, we'll apply Ajax when the class is submitted:

          $form.on('submit', function(e) {   if ($form.hasClass('is-uploading')) render imitation;    $course.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 form from being submitted repeatedly (render 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 modern browsers

If this was a grade without a file upload, we 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 piece of work, we tin use our existing isAdvancedUpload test, because the browsers which support the stuff I wrote before, likewise back up file uploading via XMLHttpRequest. Here'due south code that works on IE 10+:

          if (isAdvancedUpload) {   eastward.preventDefault();    var ajaxData = new FormData($form.become(0));    if (droppedFiles) {     $.each( droppedFiles, part(i, file) {       ajaxData.append( $input.attr('proper noun'), file );     });   }    $.ajax({     url: $form.attr('activeness'),     blazon: $form.attr('method'),     data: ajaxData,     dataType: 'json',     cache: false,     contentType: false,     processData: false,     complete: function() {       $form.removeClass('is-uploading');     },     success: function(data) {       $form.addClass( data.success == true ? 'is-success' : 'is-error' );       if (!data.success) $errorMsg.text(information.error);     },     error: function() {       // Log the error, show an alert, whatever works for you     }   }); }        
  • FormData($form.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 volition be submitted via Ajax
  • information.success and data.error are a JSON format answer which will be returned from the server. Here's what that would be like in PHP:
          <?php   // ...   die(json_encode([ 'success'=> $is_success, 'mistake'=> $error_msg])); ?>        

Ajax for legacy browsers

This is substantially for IE ix-. Nosotros do not need to collect the dragged & dropped files because in this case (isAdvancedUpload = false), the browser does not support drag & drop file upload and the form relies only on the input[type="file"].

Strangely enough, targeting the form on a dynamically inserted iframe does the play tricks:

          if (isAdvancedUpload) {   // ... } else {   var iframeName  = 'uploadiframe' + new Date().getTime();     $iframe   = $('<iframe name="' + iframeName + '" style="brandish: none;"></iframe>');    $('trunk').suspend($iframe);   $form.attr('target', iframeName);    $iframe.ane('load', part() {     var data = JSON.parse($iframe.contents().find('body' ).text());     $class       .removeClass('is-uploading')       .addClass(data.success == truthful ? 'is-success' : 'is-error')       .removeAttr('target');     if (!data.success) $errorMsg.text(information.error);     $course.removeAttr('target');     $iframe.remove();   }); }        

Automated Submission

If you have a simple course with only a drag & drop area or file input, information technology may be a user convenience to avoid requiring them to press the push. Instead, you can automatically submit the course on file driblet/select by triggering the submit event:

          // ...  .on('drop', function(eastward) { // when drag & drop is supported   droppedFiles = eastward.originalEvent.dataTransfer.files;   $form.trigger('submit'); });  // ...  $input.on('change', part(e) { // when elevate & driblet is Non supported   $form.trigger('submit'); });        

If elevate & drop expanse is visually well-designed (it's obvious to the user what to do), you might consider hiding the submit button (less UI tin exist good). Just be careful when hiding a control similar that. The button should be visible and functional if for some reason JavaScript is not available (progressive enhancement!). Adding a .no-js class proper noun to and removing information technology with JavaScript will do the trick:

          <html class="no-js">   <head>     <!-- remove this if you lot utilize Modernizr -->     <script>(function(e,t,northward){var r=east.querySelectorAll("html")[0];r.className=r.className.replace(/(^|\s)no-js(\s|$)/,"$1js$2")})(certificate,window,0);</script>   </head> </html>        
          .box__button {   display: none; } .no-js .box__button {   display: block; }        

Displaying the Selected Files

If you lot're non going to do auto-submission in that location should exist an indication to the user if they have selected files successfully:

          var $input    = $form.find('input[type="file"]'),     $label    = $form.find('label'),     showFiles = function(files) {       $characterization.text(files.length > i ? ($input.attr('information-multiple-caption') || '').replace( '{count}', files.length ) : files[ 0 ].proper noun);     };  // ...  .on('driblet', function(eastward) {   droppedFiles = e.originalEvent.dataTransfer.files; // the files that were dropped   showFiles( droppedFiles ); });  //...  $input.on('change', function(e) {   showFiles(e.target.files); });        

When JavaScript Is Non Available

Progressive enhancement is near the idea that a user should be able to complete the primary tasks on a website no matter what. File uploading is no exception. If for some reason JavaScript is not available, the interface will look similar 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'due south how information technology 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 "truthful" or "false" if upload was unsuccessful;       $upload_error – an error message of if upload was unsuccessful;     */   }  ?>        

And some adjustments for the markup:

          <form class="box" method="postal service" action="" enctype="multipart/form-data">    <?php if ($upload_success === null): ?>    <div class="box__input">     <!-- ... -->   </div>    <?php endif; ?>    <!-- ... -->    <div course="box__success"<?php if( $upload_success === true ): ?> style="display: block;"<?php endif; ?>>Done!</div>   <div class="box__error"<?php if( $upload_success === false ): ?> style="brandish: block;"<?php endif; ?>>Fault! <span><?=$upload_error?></bridge>.</div>  </form>        

That's it! This already-long commodity could have been even longer, just I think this will go y'all going with a responsible drag and drop file upload feature on your ain projects.

Check out the demo for more (view source to see the no-jQuery-dependency JavaScript):

View Demo

bauerhatin1960.blogspot.com

Source: https://css-tricks.com/drag-and-drop-file-uploading/

0 Response to "Click or Drag to Upload How to"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel