Multiple File Upload Magic With Unobtrusive Javascript

19 June 2006 | Tutorials | 39 Comments

From the creator of "15 Days of jQuery" (me):

Warning - the demos are for this tutorial went a little crazy when I recently made a change to the jquery file. I will be working to bring the demos for this tutorial back online. Until then, don’t view the tutorials if you are using IE.


Many months ago, when trying to catch up to all the AJAX hype, I was surfing the FiftyFourEleven site looking at wonderful examples of innovative javascript code when I stumbled up some code for “Upload Multiple Files With A Single File Element“.

So when I decided to launch 15 Days of jQuery, this was one of the very first scripts that I wanted to give a jQuery makeover.

Enter the Accessibility Zealots

Looking through my server logs a few days ago I see that I’ve got a trackback from a site I don’t recognize. I check it out only to discover that two of my jQuery tutorials are being listed as an example of what the author hates about javascript.

According to this twerp, any tool or technique that doesn’t put accessibility as the numero uno priority is bad.

Although I strongly disagree with this widely held belief, it got me thinking about this particular tutorial. I went back to the drawing board and created a way to create a similar effect unobtrusively… so that if my critic decides to visit with javascript turned off, he’ll be able to use the form too.

Two Tutorials for The Price of One

Goal #1: To permit multiple file uploads using one file input element… and to make the whole interaction sexy.

Goal #2: To make multiple file uploads sexy… without sacrificing usability. The focus here is unobtrusive javascript to shape a form with multiple file input fields.

The Examples

Demonstration one - only one file input element, but with the addition of jQuery and some custom code that you’re about to see it becomes a user-friendly multiple file upload script.

Multiple file upload with one file input

Demonstration two - multiple file input elements in the (x)html source of the form but jQuery modifies what the visitor sees and gives a similar feel to the first demonstration. The advantage to this method is that it is unobtrusive… with javascript turned off the visitor can still upload multiple files.

Multiple file upload with several file inputs

Explanation

Single file input -

The jQuery $(document).ready() function does two things:

Creates a div where the maximum files allowed is shown to the visitor.
Finds the file upload field (assuming there is only one) and attaches an onChange event to it.

$("input[@type=file]").change(function(){
doIt(this, fileMax);
});

The doIt() function (nice name, huh?) checks to see if the maximum file limit has been reached, and if not, it hides the file input field, adds a new one inside the containing div, puts the name of the file chosen inside the div with id “files_list”, and adds a Delete button on the end.

To navigate the DOM tree I use jQuery’s parent() function and then remove elements using the remove() function. I also make use of append() and prepend() to add in the file names and new input fields respectively.

Two key points:

1- You determine the maximum number of files allowed with this line

var fileMax = 3;

2- The file input fields must be named appropriately

<input type="file" class="upload" name="fileX[]"
  />

I do this so that the fields can be added and removed by the visitor without any concern for keeping track of id’s or names. When the form is submitted to a server side script, the information is sent in an array that can be easily traversed.

Multiple file input -

This was was trickier to pull off.

First, the number of files allowed is determined by the number of file input fields in your (x)html. Second, you should still name them in a way that stacks the field information into an array.

<input type="file" class="upload" name="fileX[]"
  />

The big difference in this second version is that I loop through each file input field and apply the doIt() function when the field value changes. By looping through each one, I can send an additional piece of information that’s critical to my code: the order of the field in the “stack”.

In other words, as the code executes, it’s specifically targeting the first input field, or the second, or the third.

The code for this is found here:

$("input[@type=file]:nth-of-type("+n+")")

jQuery’s flexibility allows me to use CSS and XPath descriptions to target specific elements.

You’ll notice that as each file is chosen, the file input field is replaced by the name of the file. Clicking on the name of the file allows you to choose a different file.

Technorati Tags: , , , ,

Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages.

Subscribe

Email Updates:
Email:
RSS Feed:

RSS information | Email Policy | RSS to Email by Aweber

39 Comments

  1. Antonio said on 20 Jun 2006 at 6:49 am:

    Perfect. Right when I needed to upload files you publish this. I’m happy :)

    Typo: “mutliple” in “Technorati tags”

  2. Jack said on 20 Jun 2006 at 9:10 am:

    Thanks for picking up on that typo… it’s fixed now.

  3. fartikus said on 21 Jun 2006 at 12:27 am:

    not sure if i would refer to an accessibility zealot as a “twerp”…and turning off javascript is not just an accessibility issue. i run the noscript extension. everyone should. who doesn’t?? its not that i refuse to allow js to execute, but i won’t allow third-party domain code, and i won’t allow code i don’t see an appropriate benefit in.

  4. fartikus said on 21 Jun 2006 at 12:34 am:

    by the way, the xpath selector - very powerful. do other toolkits support this?

    jquery strikes me as the perl of js toolkits - while the syntax may appear noisy to some, it in fact has incredible economy and some nice idioms

  5. Jack said on 21 Jun 2006 at 11:17 am:

    Fartikus,
    Just to be clear, I was not saying that anyone interested and enthusiastic about accessibility is a twerp. But this particular blogger is - in my opinion.

    The tutorials he was bashing clearly point out the accessibility issues and advise the reader to decide for himself if the benefits of the prescribed technique outweigh the downside.

    Besides, one of the main goals of this blog is to introduce the world to jQuery, show some of the unique features, and how it can be applied.

  6. tripdragon said on 22 Jun 2006 at 8:52 am:

    it’s cute but soooo old… Can you post and example of a drag to box so we can just drag a bunch of files to the window and have those upload ??

    Far faster and less fiddly

  7. Jack said on 22 Jun 2006 at 9:06 am:

    tripdragon,
    Glad you came by even though my tutorial didn’t meet your expectations.

    First, do you have a link to an example that is currently written using a different javascript library? Or are you proposing something you haven’t seen before.

    Just thinking out loud, I think you’d have to open up the age-old “browse…” button and then go navigate to find the file on your hard drive.

    A quick Google search seems to support this. I see mention of JAVA applets and situations for users with Firefox that make specific changes to their browser options.

  8. Matthijs said on 8 Jul 2006 at 4:18 am:

    “it’s cute but soo old”
    Drag and drop in this situation would not be better, in my opinion.

    Say you’re on a website on which you can upload your pictures. You’d have to resize your browser to make room for the windows(?) explorer, which you have to open also if it isn’t already. Then in the explorer you’d have to browse to the directory and files you’d want uploaded. Then you’d have to resize both windows so it is possible to drag and drop. Then you’d have to perform the actual drag and drop. How many steps are this?

    I’m not even talking about people who wouldn’t understand the whole process, don’t want or cannot use a mouse, etc

    Clicking “browse” and getting a popup to go to the file you’d want to upload is a common way which most people are familiar with nowadays.

  9. GalneP said on 17 Sep 2006 at 7:39 pm:

    Very cool widget. Thank you for your great tutorials.

    One note. As far as I can tell the demo doesn’t seem to work with the latest jQuery release:

    * $Date: 2006-08-31 13:26:31 -0400 (Thu, 31 Aug 2006) $
    * $Rev: 249 $

    As far as I can tell (and I’m pretty new to JavaScript not to mention jQuery) the following line…

    var fileName = $(”input[@type=file]:nth-of-type(”+n+”)”).val();

    is assigning “null” to fileName. Anyway, I can’t complain. In fact I’d love to see “another 15 days of jQuery”.

  10. Jack said on 18 Sep 2006 at 4:36 pm:

    Galne,
    Thanks for the compliments.

    Demo1 does work with the latest jquery. I just uploaded the latest (same one you refer to) and it works fine. The second one breaks.

    But no, the line you reference is not setting the value to null. In the jquery documentation is specifies that val() grabs the value, val(’foo’) sets the value to foo.

  11. austin said on 18 Sep 2006 at 9:44 pm:

    In the 1st demo, why is it that when i delete the (queued) files, most of the times, the counter does not revert or deduct from the current file count, thus, prompting me that I have max’ed out the no. of permitted files (for upload) when in fact, i have deleted them. Also, I’m having some problems with IE6.

    Pretty sleek tutorials though!

  12. Jack said on 18 Sep 2006 at 10:24 pm:

    Austin,
    After some testing, I was able to reproduce it under a specific situation: if you maxed out the number of files, got the error message, and then went to delete and then re-add files, you got a an error, when in fact you shouldn’t. (confused yet?)

    I was missing a return true;

    function doIt(obj, fm) {
    if($(’input.upload’).size() > fm) {alert(’Max files is ‘+fm); obj.value=”;return
    true;}
    

    And darn it… I see what you mean about IE. Crap. I’ll have to take this tutorial down until I figure out what the deal is. I updated the jquery.js file to the latest and things went screwy.

  13. George said on 5 Oct 2006 at 6:34 am:

    The fault with the demo may be linked to the use of the :nth-of-type() selector that is not in the recent jquery releases. You may be able to use :nth-child() instead (though it won’t distinguish between input types), or use :nth-of-type() available in the moreSelectors jQuery plugin at http://www.softwareunity.com/sandbox/JQueryMoreSelectors. (Just a suggestion. I’ve not studied your code)

  14. Jack said on 6 Oct 2006 at 1:16 am:

    George,
    You’re the bomb!

    I don’t know why I didn’t see the reson why before. Apparently nth-of-type didn’t make the cut when it was recently decided what was core and what wasn’t.

    Here’s one of the fixed demos.

    http://15daysofjquery.com/examples/jqueryMultiFile/demo4.php

    (Crossing that one off the list!)

    Thanks again.

  15. Philip said on 30 Oct 2006 at 1:34 am:

    I tried the demo (firefox 1.507) and uploaded 3 dummy files. Then clicked on reset. It created an extra reset button, up to at least six of them.

    Next question: is there a way to add an upload progress meter to tell me how much or how many files have been uploaded during the process?

    Wonderful work! :-)

  16. Jack said on 30 Oct 2006 at 11:36 am:

    Progress demo generally can’t be done using PHP. I’ve researched it pretty extensively, and although I don’t have a link handy, if you Google it you’ll see that there are very few workarounds, most of them involving a java applet.

  17. Vince said on 17 Dec 2006 at 7:46 pm:

    uhmm i know you disabled it from actually uploading the files.. i think.. i’m kind of a noob but this is a great script. The best file uploader i have found and… would like to know how to finish it so it uploads the files?

  18. Jack said on 19 Dec 2006 at 10:39 pm:

    Vince,
    Yes, the upload capability is disabled in the demo.

    I have a contact form processor script that does a great job handling file uploads.

    http://www.ultimateformmail.com/jquery-special.php

  19. Nate said on 7 Feb 2007 at 12:07 pm:

    Any estimate on when the second demo might be working again? Also, might it be possible to see a demo that loads a thumbnail of each image as they are uploaded? Many thanks for what you have done already. This looks very exciting.

  20. Jack said on 7 Feb 2007 at 2:44 pm:

    Right around the corner, in a nice, neat plugin. Thumbnails… hmmm, not as a demo, sorry.

  21. Nate said on 7 Feb 2007 at 3:38 pm:

    Would the thumbnail feature be difficult to code? Any hints or ideas on how to do that? Has it been done before? I’ve searched the web for an example or demo script that can do this, but I’ve not found any so far. There was one javascript that worked in IE 6 only, but who uses IE 6?

  22. Jack said on 7 Feb 2007 at 3:50 pm:

    Thumbnail can’t be generated until the file is uploaded to the server. If you are uploading three files, they get sent at the same time, under normal conditions.

    The thumbnail generation is PHP, server side. Javascript can’t do this alone.

    Sorry.

  23. Nate said on 8 Feb 2007 at 3:29 pm:

    Right, you need to use both a server side script along with Javascript together to do this. I have a function in Coldfusion that will create the thumbnail. The part that I can’t figure out is how to use jquery to submit the file to my Coldfusion script and then create the img src html to show the thumbnail without refreshing the page. I imagine you could use an iframe and some jquery code to do this, but I’m not sure how. If anyone has tip or suggestion for me, I’d love to hear it.

  24. Oli said on 8 Mar 2007 at 2:01 pm:

    the IE-Problem drove me crazy!
    the solution for demo1.php …

    …find(”input:visible”).change(function(){ doIt(this);
    });

    (bind the new change-event on the visible input)

  25. Nate said on 26 Mar 2007 at 7:09 pm:

    Has anyone seen the swfupload: http://swfupload.mammon.se/
    This is awesome because you can shift click to upload many fils at once. I’d love to see this one redone with jquery instead of Javascript. Anyone want to do that and post the jquery code for the rest of us to use?

  26. Jack said on 27 Mar 2007 at 10:41 am:

    Nate,
    I haven’t seen that one. I have a plugin that comes close, but doesn’t do the shift click. It opens a normal browse window. I suspect that Flash is what gives that other code the extra functionality.

    By the way, jQuery is a javascript library.

  27. Nate said on 27 Mar 2007 at 11:25 am:

    Flash is indeed what is used there. I’d love to see an example that didn’t require Flash for the shift click feature to work, but I’m not sure it exists in the open source world yet.

  28. Diego said on 11 Apr 2007 at 5:23 am:

    Last week I released a plugin very similar to this. People interested on this post may also find the following useful:
    http://www.fyneworks.com/jquery/multiple-file-upload/

  29. Jack said on 11 Apr 2007 at 9:22 am:

    Diego,
    Very cool. You beat me to it. Good job.

  30. Mike said on 13 Jun 2007 at 2:59 pm:

    I am having a big issue making anything with multifile work with safari.. does anyone see this problem and know of a solution?

  31. vid said on 21 Jun 2007 at 9:19 am:

    good scripts i going to give it a try,
    then will test for safari too ( we must make it work on safari becourse of it’s popular now come to window os )
    will get back asap

  32. vjeran said on 29 Aug 2007 at 6:35 am:

    Hmm.. well nice. i have made multiple files upload in flash. Now i am looking to jquery and YUI ext. Jquerry looks very simple and nice and i would add CSS framework. BUt still i don’t know what to choose.

  33. Snowman53 said on 18 Apr 2008 at 9:52 pm:

    This topic has been posted on the Thickbox forum, but no workable solution has been offered, or at least I couldn’t recognize one if it was.

    Hopefully someone can walk me through a solution!

    Here is an example of a TB call I am making (using: jquery-1.2.3, TB 3.1, IE7);
    Edit

    TB opens with the default size and located near the top of the page instead of using my sizes and centering on the screen when the page is scrolled down.

    If I use the same code without passing a variable to the destination URL, TB works as expected (fantastically).

    So clearly passing the URL variable messes up passing variables to TB. Could someone walk me through how to change this so it works correctly?

    Note that the URL variable is generated by the ASP code and is not easily modified, so I need a Thickbox oriented solution (if possible).

    Thanks!

  34. manh ha said on 23 Apr 2008 at 5:55 am:

    I found some bugs, if play it in ie6 when choose one file, it auto add o max.
    To fix this problem REPLACE:

    $(obj).parent().prepend(”).find(”input”).change(function() {doIt(this, fm)});

    BY

    $(obj).parent().prepend(”);

  35. manh ha said on 23 Apr 2008 at 5:57 am:

    $(obj).parent().prepend(”);

  36. manh ha said on 23 Apr 2008 at 5:57 am:

    oopts fix html text

  37. manh ha said on 23 Apr 2008 at 5:59 am:

    $(obj).parent().prepend(’input type=”file” class=”upload” name=”fileX[]” onchange=”doIt(this, ‘+fm+’);”‘);

  38. Marakas said on 20 May 2008 at 2:58 am:

    @Oli -> Thanks for providing the ie fix, that was driving me mad as well.

  39. scripter said on 20 Jun 2008 at 5:10 am:

    @Oli too - thanks for the ie fix

Leave a Reply

You can follow the discussion through the Comments feed. You can also pingback or trackback from your own site.