EUCOOKIELAW_BANNER_TITLE

  • I think that

    Making the Web is like playing a game. Standard, Accessibility and Usability are only few rules.
    No game is awesome if you don't follow the rules.

    International Webmasters Association

  • Subscribe to my blog via email

    Insert here your e-mail address and you will receive a message when a new post will come.

Multiple files download on single link click

Sometimes happens that some people need to download multiple file from a single user interaction: the click on the link.

Assuming that your  browser suppors Javascript and considering to use jQuery to simplify the concepts, you can use a simple script to accomplish this job.

The basis

The concept behind to our script is that we have a link on the page that should react with the download of multiple files. Those files are generated from page content’s processing as data URI. Further, they could be either a file on the server or a client generated content as data URI, but it is not a critical information for our goal.

The solution

The script that does to this job to work properly is listed below:

function downloadAll(files){
    if(files.length == 0) return;
    file = files.pop();
    var theAnchor = $('<a />')
        .attr('href', file[1])
        .attr('download',file[0]);
    theAnchor[0].click(); 
    theAnchor.remove();
    downloadAll(files);
}

It expects that the files parameter must be a bidimentional array as a grid of infinite row. Each row must be defined with 2 columns, the first column must be the file name and the second must contain the file content  specified as dataURI (if your browser supports it) or a link to the file to download.

An hypotetic call to the above code must be:

downloadAll([
    ['file1.csv', 'data:text/csv;charset=utf8,'+
                  encodeURIComponent('my,csv,file\and,so,on')],
    ['file2.txt', 'data:text/plain;charset=utf8,'+
                  encodeURIComponent('this script can do what I need.')],
    ['file3.js', 'data:text/javascriptcharset=utf8,'+
                  encodeURIComponent('alert(\'You can donate me your house if you like this script :-) \')')]
]);

Code analysis

As you can see the method downloadAll will call itself recursively for the number of files defined in the given array ( row 9 and row 2).

The pop() method called on the array will return as output the last item and remove it from the array ( row 3).

Then the method creates a link with the download and the href attributes. The link is not attached to the DOM to avoid the well known browser reflow problem ( rows 4 – 6 ).

Here comes the odd part of the script, the one that makes me crazy to exactly understand how to make it to work as expected.

The jQuery event behavior

As I’ve mentioned at the beginning of this post, we are using jQuery (and it’s obvious, reading the code), and the first think I’ve tried is to fire the click event using the jQuery click method. Well, I’m asking you to do a simple experimnent.

Create an empty HTML page, then insert a link on it (<a id="open-google" href="http://www.google.it">Open google page</a> is just what we need).

Well if you make a click on the link with your pointer device, the browser behavior is as expected that your page will change to the one defined in the link.

But what about if you do something similar with jQuery? Well, the answer is in your hands!

Try to write this simple Javascript, if possible in DOMReady event or at the end of the page (Remember to include jQuery).

jQuery('#open-google').click();

Don’t worry it isn’t your script fault. Indeed if you attach a jQuery event to the link, you will obviously see that the code is correctly executed. Then, why the click is not working as expected opening the URL?

I was reading jQuery documentation about click and trigger but nothing has told me that this is the right behavior. However if click method does not work is because it’s a shortcut call for trigger(‘click’) method.

From jQuery click method documentation page: This method is a shortcut for .on( "click", handler ) in the first two variations, and .trigger( "click" ) in the third. The click event is sent to an element when the mouse pointer is over the element, and the mouse button is pressed and released.

Reading into the source code I can read this comment:

// For cross-browser consistency, don't fire native .click() on links

But we need to fire the native click exactly on links, so the only way to do this is to get the DOM node and invoke the native click method ( look at the row 7) . After the call we can remove the link that is unuseful anymore ( row 8).

The Firefox issue

But again, it’s not ended here, if you are running the script on firefox (tested  on FireFox 33) it seems that does not work. Well what I’ve discovered is that Firefox does not fires click if the link is not attached to the DOM. So we cannot avoid the browser reflow.

The final result is shown in the fiddle that I’ve prepared for you.