How to Put Uploaded Files Into a Folder Javascript Ajax Codepen
I work on an RSS reader app chosen Readerrr (editor'due south note: link removed as site seems expressionless). I wanted to enrich the feed import experience by making allowing for drag and drop file upload aslope the traditional file input. Sometimes drag and drop 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 driblet. It'southward only a normal, functional<grade>
, albeit with some extra HTML elements for potential states.
<form class="box" method="post" action="" enctype="multipart/form-data"> <div class="box__input"> <input class="box__file" blazon="file" name="files[]" id="file" data-multiple-explanation="{count} files selected" multiple /> <label for="file"><strong>Choose a file</strong><bridge class="box__dragndrop"> or drag information technology here</bridge>.</label> <button grade="box__button" type="submit">Upload</button> </div> <div class="box__uploading">Uploading…</div> <div course="box__success">Done!</div> <div class="box__error">Error! <bridge></span>.</div> </form>
We'll hide those states until nosotros need them:
.box__dragndrop, .box__uploading, .box__success, .box__error { display: none; }
A little caption:
- Regarding states:
.box__uploading
element volition be visible during the Ajax process of file upload (and the others will yet be subconscious). Then.box__success
or.box__error
volition exist shown depending on what happens. -
input[type="file"]
andlabel
are the functional parts of the grade. I wrote about styling these together in my post nearly customizing file inputs. In that post I also described the purpose of[data-multiple-caption]
attribute. The input and label also serve equally an alternative for selecting files in the standard way (or the only mode if drag and drop isn't supported). -
.box__dragndrop
will be shown if a browser supports drag and drib file upload functionality.
Feature detection
Nosotros can't 100% rely on browsers supporting drag and drop. Nosotros should provide a fallback solution. And then: feature detection. Drag & drop file upload relies on a number of dissimilar JavaScript API's, and then we'll need to check on all of them.
Starting time, drag & drop events themselves. Modernizr is a library you can trust all about feature detection. This examination is from in that location:
var div = document.createElement('div'); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)
Next we demand to bank 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 one is a bit catchy considering there is no bullet-proof style to discover the availability of the object before user's first interaction with the drag & drop interface. Not all browsers expose the object.
Ideally we'd like to avoid UX similar…
- "Drag and drib files here!"
- [User drags and drops files]
- "Oops simply kidding elevate and drib isn't supported."
The play tricks here is to check the availability of FileReader API right when the document loads. The idea backside this is that browsers that support FileReader
support DataTransfer
too:
'FileReader' in window
Combining the lawmaking above into self-invoking anonymous function…
var isAdvancedUpload = office() { var div = document.createElement('div'); render (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; }();
… will enable usa to make an effective characteristic support detection:
if (isAdvancedUpload) { // ... }
With this working feature detection, now we can allow the users know they can drag & drop their files into our course (or not). Nosotros tin can style the form by adding a class to it in the case of support:
var $form = $('.box'); if (isAdvancedUpload) { $form.addClass('has-advanced-upload'); }
.box.has-avant-garde-upload { background-color: white; outline: 2px dashed black; outline-offset: -10px; } .box.has-advanced-upload .box__dragndrop { display: inline; }
No problems at all if elevate & drop file upload is non supported. Wsers will be able to upload files via good ol' input[type="file"]
!
Notation on browser support: Microsoft Border has a bug which stops elevate and drop from working. Information technology sounds like they are aware of information technology and hope to gear up it. (Update: link to bug removed as the link stopped working. At present that Border is Chromium, presumably, it'south non a problem anymore.)
Elevate 'n' Drib
Here we go, hither'due south the good stuff.
This part deals with calculation and removing classes to the course on the different states like when the user is dragging a file over the class. And so, catching those files when they are dropped.
if (isAdvancedUpload) { var droppedFiles = false; $grade.on('drag dragstart dragend dragover dragenter dragleave driblet', office(e) { e.preventDefault(); e.stopPropagation(); }) .on('dragover dragenter', function() { $form.addClass('is-dragover'); }) .on('dragleave dragend drop', function() { $form.removeClass('is-dragover'); }) .on('drop', function(east) { droppedFiles = due east.originalEvent.dataTransfer.files; }); }
-
eastward.preventDefault()
andeastward.stopPropagation()
prevent any unwanted behaviors for the assigned events beyond browsers. -
due east.originalEvent.dataTransfer.files
returns the list of files that were dropped. Shortly you lot volition see how to use the information for sending these files to the server.
Calculation 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-color: grey; }
Selecting Files In a Traditional Way
Sometimes dragging & dropping files is not very comfortable way for selecting files for upload. Especially when a user is in front of a small screen size computer. Therefore it would be nice to let users cull the method they adopt. The file input and label are here to allow this. Styling them both in the way I've described allows us to go on the UI consistant:
Ajax Upload
There is no cantankerous-browser way to upload dragged & dropped files without Ajax. Some browsers (IE and Firefox) do not permit setting the value of a file input, which then could be submitted to server in a usual way.
This won't work:
$grade.discover('input[type="file"]').prop('files', droppedFiles);
Instead, we'll use Ajax when the form is submitted:
$grade.on('submit', role(e) { if ($form.hasClass('is-uploading')) return simulated; $class.addClass('is-uploading').removeClass('is-mistake'); 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 (return fake
) 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 form without a file upload, we wouldn't need to have two dissimilar Ajax techniques. Unfortunately, file uploading via XMLHttpRequest
on IE 9 and below is not supported.
To distinguish which Ajax method volition work, we can use our existing isAdvancedUpload
test, considering the browsers which support the stuff I wrote before, also back up file uploading via XMLHttpRequest. Hither'southward code that works on IE 10+:
if (isAdvancedUpload) { e.preventDefault(); var ajaxData = new FormData($class.get(0)); if (droppedFiles) { $.each( droppedFiles, part(i, file) { ajaxData.append( $input.attr('proper noun'), file ); }); } $.ajax({ url: $form.attr('action'), type: $class.attr('method'), information: ajaxData, dataType: 'json', cache: false, contentType: fake, processData: faux, complete: function() { $form.removeClass('is-uploading'); }, success: function(information) { $grade.addClass( data.success == truthful ? 'is-success' : 'is-mistake' ); if (!data.success) $errorMsg.text(data.mistake); }, error: function() { // Log the mistake, show an alert, whatsoever works for yous } }); }
-
FormData($form.become(0))
collects data from all the course 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.fault
are a JSON format answer which will exist returned from the server. Here's what that would be like in PHP:
<?php // ... die(json_encode([ 'success'=> $is_success, 'error'=> $error_msg])); ?>
Ajax for legacy browsers
This is substantially for IE 9-. We do not need to collect the dragged & dropped files because in this case (isAdvancedUpload = faux
), the browser does non back up drag & drop file upload and the class relies only on the input[type="file"]
.
Strangely enough, targeting the course on a dynamically inserted iframe does the trick:
if (isAdvancedUpload) { // ... } else { var iframeName = 'uploadiframe' + new Date().getTime(); $iframe = $('<iframe name="' + iframeName + '" style="display: none;"></iframe>'); $('torso').append($iframe); $form.attr('target', iframeName); $iframe.1('load', function() { var data = JSON.parse($iframe.contents().notice('trunk' ).text()); $form .removeClass('is-uploading') .addClass(information.success == truthful ? 'is-success' : 'is-fault') .removeAttr('target'); if (!data.success) $errorMsg.text(data.error); $course.removeAttr('target'); $iframe.remove(); }); }
Automatic Submission
If you have a simple form with just a drag & driblet expanse or file input, it may be a user convenience to avoid requiring them to press the button. Instead, y'all tin automatically submit the course on file drib/select by triggering the submit
consequence:
// ... .on('drop', function(east) { // when elevate & driblet is supported droppedFiles = e.originalEvent.dataTransfer.files; $form.trigger('submit'); }); // ... $input.on('alter', function(e) { // when drag & drop is NOT supported $form.trigger('submit'); });
If drag & drop area is visually well-designed (it'due south obvious to the user what to exercise), you might consider hiding the submit push button (less UI tin can be proficient). Simply be careful when hiding a command like that. The push 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 it with JavaScript will do the trick:
<html grade="no-js"> <head> <!-- remove this if you apply Modernizr --> <script>(function(e,t,due north){var r=e.querySelectorAll("html")[0];r.className=r.className.supplant(/(^|\s)no-js(\southward|$)/,"$1js$two")})(document,window,0);</script> </head> </html>
.box__button { brandish: none; } .no-js .box__button { brandish: block; }
Displaying the Selected Files
If you're non going to do machine-submission at that place should be an indication to the user if they have selected files successfully:
var $input = $form.detect('input[type="file"]'), $label = $course.find('label'), showFiles = function(files) { $label.text(files.length > 1 ? ($input.attr('data-multiple-caption') || '').replace( '{count}', files.length ) : files[ 0 ].name); }; // ... .on('drib', part(e) { droppedFiles = due east.originalEvent.dataTransfer.files; // the files that were dropped showFiles( droppedFiles ); }); //... $input.on('change', role(e) { showFiles(e.target.files); });
When JavaScript Is Not Available
Progressive enhancement is about the idea that a user should be able to consummate the master tasks on a website no thing what. File uploading is no exception. If for some reason JavaScript is not bachelor, the interface will look like this:
The page volition refresh on grade submission. Our JavaScript for indicating the issue of submission is useless. That means nosotros take to rely on server-side solution. Here'due south 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 "fake" if upload was unsuccessful; $upload_error – an error message of if upload was unsuccessful; */ } ?>
And some adjustments for the markup:
<form class="box" method="post" activeness="" enctype="multipart/form-data"> <?php if ($upload_success === nada): ?> <div class="box__input"> <!-- ... --> </div> <?php endif; ?> <!-- ... --> <div class="box__success"<?php if( $upload_success === true ): ?> style="brandish: block;"<?php endif; ?>>Done!</div> <div course="box__error"<?php if( $upload_success === simulated ): ?> way="brandish: cake;"<?php endif; ?>>Mistake! <bridge><?=$upload_error?></bridge>.</div> </form>
That'due south it! This already-long article could have been even longer, only I retrieve this will get you going with a responsible drag and driblet file upload characteristic on your ain projects.
Check out the demo for more (view source to run into the no-jQuery-dependency JavaScript):
View Demo
Source: https://css-tricks.com/drag-and-drop-file-uploading/