Drag and Drop MooTools File Uploads

By  on  

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!

Recent Features

  • By
    Responsive and Infinitely Scalable JS Animations

    Back in late 2012 it was not easy to find open source projects using requestAnimationFrame() - this is the hook that allows Javascript code to synchronize with a web browser's native paint loop. Animations using this method can run at 60 fps and deliver fantastic...

  • By
    CSS vs. JS Animation: Which is Faster?

    How is it possible that JavaScript-based animation has secretly always been as fast — or faster — than CSS transitions? And, how is it possible that Adobe and Google consistently release media-rich mobile sites that rival the performance of native apps? This article serves as a point-by-point...

Incredible Demos

  • By
    Introducing MooTools ScrollSidebar

    How many times are you putting together a HTML navigation block or utility block of elements that you wish could be seen everywhere on a page? I've created a solution that will seamlessly allow you to do so: ScrollSidebar. ScrollSidebar allows you...

  • By
    CSS pointer-events

    The responsibilities taken on by CSS seems to be increasingly blurring with JavaScript. Consider the -webkit-touch-callout CSS property, which prevents iOS's link dialog menu when you tap and hold a clickable element. The pointer-events property is even more JavaScript-like, preventing: click actions from doing...

Discussion

  1. emehrkay

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

    You didn’t link to Arian’s classes though

    • Added the link! No idea how I missed that!

  2. 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. 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. 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. 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

    • 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! :)

    • 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. 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. can i please have a copy of your source code?
    http://davidwalsh.name/demo/mootools-upload.php

  14. sd

    where is the source code ! i tried to try make new uploader ! link ? link ?

  15. works great, progress indicator is there but integer percent showing would be great idea too.

  16. Noura

    Great job !! congratulations!! could you post a link to download the source code?
    Please i need it .
    Thanks in advance.

  17. Julius

    I’m using your library to be able to upload videos in YouTube through XMLHttpRequest.

    I can successfully upload a video but after uploading Youtube respond with a HTTP 302 header, how can I follow the redirection url or how can I get the redirection url?

    I hope someone has a solution for this.

  18. mw

    Has anyone all files? I´m missing the mootools-upload.php… Can anyone send me all the files to “wick.moritz{at}gmail.com” ? Thanks!

  19. MAK

    here is demo code and script files
    https://github.com/arian/mootools-form-upload

  20. Can I make upload for single file ? How to do it? Thanks

  21. Rich

    I’m not able to use this either. Seems that the files.php only has 8 lines of code. Looking at the version comments, it looks like it should be doing the work of saving the files, but isnt doing anything.

    Please give us the contents of files.php…

  22. Doesn’t seem to work with any version of IE. IE10 returns true to (‘FormData’ in window) but you can’t drop your files. So, that being as it is I altered the code just a bit:

    window.addEvent('domready', function(){
        if (!Browser.ie) {    
    	    // Create the file uploader
        	var upload = new Form.Upload('file', {
    	    	dropMsg: "Drop files here",
    		    onComplete: function(){
    			    alert('Files uploaded!');
        		}
    	    });
        }
    
        if ((Browser.ie) || (!upload.isModern()) {
    	    new iFrameFormRequest('uploadForm', {
    		    onComplete: function(response){
    			    alert('Files uploaded!');
            	}
    	    });
        }
    });
    
  23. Paul

    Hi David,

    Thanks for this wonderful script. I would like to send extra parameters to the upload page. I tried using vars parameter but the script not working as expected. I have passed the values from hidden input value. Can you pls explain how can we send the extra parameters to the uploader page ?

  24. Great post as always. I use this upload system. One thing I noticed was some commenters looking for info on how to process the file at the php side. I suggest a very good place to start is here:

    http://www.sitepoint.com/file-uploads-with-php

    It explains the issues clearly and importantly give you an idea how to deal with the important security issues associated with file upload.

  25. David Huynh

    I tried the sample code given but unable to get it working.

    I basically wrote a PHP class to handle all the generating the form and actions for it.

    class HCFileUpload 
    {
        private $upload_directory = "/tmp/";
        
        /**
         * Class constructor
         */
        public function __construct( $upload_directory = "/tmp/" )
        {
            if ( !is_writable( $upload_directory ))
            {
                $_SESSION['debug'] = "Directory " . $upload_directory . " is not writable";
                exit();
            }
            
            $this->upload_directory = $upload_directory;
        }
        
        /**
         * Gets the header of the object for this feature to work
         * @return string HTML string that gets inject into the header of the current page
         */
        public function get_header()
        {
            $ret_value = 
            '        
            
            
            
                    
            
    
            
            window.addEvent("domready", function()
            {
                var upload = new Form.Upload("url", 
                {
                    onComplete: function()
                    {}
                });
    
                if ( !upload.isModern() )
                {
                    // not modern browser
                }
            });
    
             
            ';
            
            return $ret_value;
        }
        
        
        /**
         * Gets the upload form
         * @return string Retrieves the HTML for the upload form
         */
        public function display_form()
        {
            $ret_value =
            '
            
                
                
                    Upload Files
    
                    
                        Select File To Upload: 
                        
                    
    
                    
                        
                    
                
                
                
            ';
            
            return $ret_value;
        }
        
        /**
         * Performs the actual actions when files had been selected for upload
         * @return boolean TRUE and message if the files had been uploaded successfully, FALSE otherwise
         */
        public function perform_action()
        {
            $ret_value = FALSE;
            
             $ret_value = "Files uploaded successfully !!!";       
            
            if( count( $_FILES['uploads']['url'] ) ) 
            {
                echo "Total files uploaded is " . count( $_FILES['uploads']['url'] ) . "";
                
                foreach ($_FILES['uploads']['url'] as $file) 
                {
    
                    //do your upload stuff here
                    echo $file;
    
                }
            }
            
            /*
            if ( isset( $_FILES['url'] ) )
            {
                //$ret_value = "Files uploaded found";
                
                echo "" . print_r( $_FILES['url']['tmp_name'], true ) . "";
                
                foreach ( $_FILES['url']['tmp_name'] as $key => $tmp_name )
                {
                    $file_name = $key . $_FILES['url']['name'][$key];
                    $file_size = $_FILES['url']['size'][$key];
                    $file_tmp = $_FILES['url']['tmp_name'][$key];
                    $file_type = $_FILES['url']['type'][$key];  
                    
                    // move upload files to proper place
                    move_uploaded_file( $file_tmp, $this->upload_directory . $file_name );
                    
                    $ret_value = "Files uploaded successfully";
                }
                
            } */
            
            return $ret_value;
        } 
        
        /**
         * General function to display this module
         * @return string HTML string to display for this module
         */
        public function display()
        {
            if ( isset( $_POST['UPLOAD_FILES'] ) )
            {            
                return $this->perform_action();
            }
            else
            {
                return $this->display_form();
            }
        }      
    }
    

    And inside Form.Upload.js, I had to comment out

    /*
    		form.addEvent('submit', function(event){
    			if (event) event.preventDefault();
    			self.submit(inputFiles, inputname, uploadReq);
    		}); */
    

    For it to call my php script after submitting the files but still unable to get the files uploaded as the $_FILES array is empty.

    Please advise. Thanks.

  26. Kushal
    onDrop: function(){
    	drop.removeClass.pass('hover', drop);
    	if (self.options.fireAtOnce){
    		self.submit(inputFiles, inputname, uploadReq);
    	}
    },
    

    in the above code inputname is the name for the file if i want to change it to image what should i change with

  27. Khalid Khan

    Hi,

    I was stuck when post my form values along with the files value, but I am unable get the form data.

    I want to implement drag drop feature in my contact form.

    Can you help me to get the form data with files data.

    Waiting for you reply.

    Thanks,
    Khalid Khan

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!