Drag and Drop MooTools File Uploads

Honesty hour confession:  file uploading within the web browser sucks.  It just does.  Like the ugly SELECT element, the file input is almost unstylable and looks different on different platforms.  Add to those criticism the fact that we're all used to drag and drop operations, yet up until recently, you couldn't drag files into a browser to upload them, making file uploading within the browser unintuitive.  With recent advancements in browser technology, the drag and drop method is now supported, but it doesn't look good without a bit of work.  Luckily MooTools Core Developer Arian Stolwijk has created a set of classes to accommodate styling drag and drop file uploading within the browser.  Let's have a look at how it works!

The HTML

The basic setup is the same as a traditional form upload within the browser;  a FORM element with an INPUT of file type:

<form method="post" action="mootools-upload.php" enctype="multipart/form-data" id="uploadForm">
<div>
	<div class="formRow">
		<label for="file" class="floated">File: </label>
		<input type="file" id="file" name="file[]" multiple><br>
	</div>

	<div class="formRow">
		<input type="submit" name="upload" value="Upload">
	</div>
</div>
</form>

This setup allows file uploading even if JavaScript is not enabled.  (Note to IT snobs:  get over yourselves and turn JavaScript back on)

The CSS

The "drop zone" and "progress bar" areas can be easily styled in any fashion you'd like.  My sample CSS looks like this:

.droppable {
	border: #ccc 1px solid;
	border-radius: 8px;
	background: #eee;
	color: #666;
	padding: 20px;
	margin: 10px;
	clear: both;
	text-align: center;
}

.droppable.hover {
	background: #ddd;
}

.uploadList {
	margin: 0;
	padding: 0;
	list-style: none;
}

.uploadItem {
	overflow: hidden;
	border-bottom: #BCBCBC 1px solid;
	margin: 0 20px;
	padding: 3px;
}

.uploadItem span {
	overflow: hidden;
	width: 150px;
	float: left;
	display: block;
}

a.addInputRow,
a.delInputRow,
.uploadItem a {
	display: inline-block;
	background: url(add.png) no-repeat;
	height: 16px;
	width: 16px;
	text-indent: -999px;
}

.uploadItem a {
	float: left;
	display: block;
	padding-left: 20px;
	background-image: url(delete.png);
}

a.delInputRow {
	background-image: url(delete.png);
}

.progress {
	margin: 5px 0;
	height: 15px;
	border-radius: 3px;
	background: #545A74;
}

Since we have (unfortunately) become accustomed to the ugly INPUT type=file, my only CSS advice is to make sure you make not only your "drop zone" apparent but explain that the user should drag and drop.

The MooTools JavaScript

Arian has provided 3 JavaScript classes within his mootools-form-upload repository:

  • Form.Upload:  The main worker class, detecting the browser capabilities and building a file uploader based on those features
  • Form.MultiFileInput:  A class which builds and manages the list of files to be uploaded.
  • Request.File:  Manages the FormData object, sends files, and reports progress.

Another resource, iFrameFormRequest, can be included in case the user is rocking a legacy browser.  With the resources above added to the page, let's set up our drag and drop file uploader:

window.addEvent('domready', function(){
	
	// Create the file uploader
	var upload = new Form.Upload('file', {
		dropMsg: "Drop files here",
		onComplete: function(){
			alert('Files uploaded!');
		}
	});

	// Use iFrameFormRequest, which posts to iFrame 
	if (!upload.isModern()) {
		new iFrameFormRequest('uploadForm', {
			onComplete: function(response){
				alert('Files uploaded!');
			}
		});
	}

});

We start by creating an instance of Form.Upload, passing it the INPUT node and the class options.  The onComplete option is most important, as it represents the event that fires when all uploads have completed, allowing you to notify the user.

For more customizable uploads, like notifications for progress and success, you can pair Form.MultipleFileInput and Request.File directly:

// From ReadMe.md

// the input element, the list (ul) and the drop zone element.
var input, list, drop;
// Form.MultipleFileInput instance
var inputFiles = new Form.MultipleFileInput(input, list, drop, {
    onDragenter: drop.addClass.pass('hover', drop),
    onDragleave: drop.removeClass.pass('hover', drop),
    onDrop: drop.removeClass.pass('hover', drop)
});

// Request instance;
var request = new Request.File({
    url: 'files.php'
    // onSuccess
    // onProgress
});

myForm.addEvent('submit', function(event){
    event.preventDefault();
    inputFiles.getFiles().each(function(file){
        request.append('url[]' , file);
    });
    request.send();
});

This solution would be good for using detailed progress bars.

Outstanding work once again by Arian.  His contribution to the MooTools JavaScript framework has been priceless, and he continues that effort with his drag and drop file upload system.  These classes prove the power of MooTools and the advancement of browsers today.  Give your users the elegant option of drag and drop uploads!


Comments

  1. emehrkay

    Great stuff. This is going into my “to implement later” bag.

    You didn’t link to Arian’s classes though

  2. Cris McLaughlin

    I recently switched from jQuery to Mootools and I have to say I really like how elegant it is. The forge alone is an unbeatable resource and the OO approach for me, as a web application developer, is just perfect. I am redesigning the image gallery for our website and was trying to find a simple and easy way to manage multiple file uploads. I originally was using Uploadify but now I have come across this jewel. Thank you for all of your contributions to the open source community and I will definitely be waiting for your next post, cheers!

  3. Christophor S. Wilson

    David, I don’t know if you know this or not, but you are my hero! :D keep up all of the great tuts, I hit up your site daily.

  4. Alex

    When David writes new articles about MooTools stuff, I’m in ecstasy.
    I love MooTools so much that if David was a female I would also love him.toFemale();
    I don’t even care if Joomla ditches MooTools in its next major version. I feel so high!

  5. wayne melrose

    Hi there,

    This looks awesome! although on trying to implement, I find that I don’t understand where the files are being uploaded to?

    Could someone point in the right direction please?

    Thanks

    • David Walsh

      They’ll be uploaded to the form’s “action” attribute / URL.

    • wayne melrose

      Thanks for the reply, I had tried that, perhaps I had some permissions issues.. Regardless, I’ll give it another go and thanks for the reply! :)

    • David Walsh

      Is the request showing up in Firebug? It should…

    • wayne melrose

      actually no.. Although I need to put this out on the web somewhere so perhaps someone else could verify.. Embarrassingly I’ve not used firebug until today :$ and I’m not sure if it’s just me mis-reading things..

      I’ll try and get something on the web and post.. here again?

  6. Greg

    I’m also having problems figuring out where the files are going…

  7. Trevoe

    “the drag and drop method is not supported” You mean the opposite =
    “the drag and drop method is now supported” I assume.

  8. Sasi

    I like this, but I unable to remove files from the collections, file names are removing from display but not from collections.

    Please do the needful

  9. Animal21

    Hi,
    very nice snippet.
    But can you give me a hint how the php-file shld look like to handle the files and save them on the server?

    kind regard
    ani

  10. Felipe

    I see that file ‘file.php’ is incomplete. The tag php dont close.

  11. Felipe Matos

    For something reason. The file ‘file.php’ isnt to loaded. That way, we cant be implementing a code for upload the files.

  12. JonathanR

    Is there any thing special i need to do to update this code to work with mootools 1.4.5 ? (Joomla 2.5 included mootools)

    The drag and drop seems to work. The images get added to the list. the delete button seems to remove the images when clicked as well.

    But when I hit my Submit button (or the upload from the example) I get this following error threw firebug :


    event is undefined
    (?)(event=undefined)Form.Upload.js (line 78)
    fireEvent(f=function())mootools-core.js (line 375)
    forEach()mootools-core.js (line 40)
    i = function()
    v = form#adminForm index.php
    fireEvent(e="submit", c=[], b=undefined)mootools-core.js (line 375)
    submitform(a="fichephotoupload.save", b=form#adminForm index.php)core.js (line 4)
    submitform(a="fichephotoupload.save")core.js (line 4)
    onclick(event=click clientX=1726, clientY=124)index....onclick (line 2)
    [Break On This Error]

    event.preventDefault();

    Something to do with: Form.Upload.js (line 78)

    any ideas?

  13. grafosecondo

    Hi David,

    just a question, I added a

    to my form, but the $_POST is empty on php side. Does this script send only files ignoring other inputs?

    Thanx for help.


Be Heard!

Share your thoughts without being a jerk! And wrap your code in <code> tags, f00!

Name*:
Email*:
Website: