Tuesday, March 19, 2024

Using the HTML5 Download Attribute

Using the HTML5 Download Attribute

Many of the HTML5 additions to the HTML landscape have been trumpeted with much fanfare. Meanwhile, a new attribute has been added to the a <a> element that has gone unnoticed by many writers and developers. It’s the download attribute and it promises to replace PHP-driven file download scripts with a completely HTML-based paradigm. In today’s article, we’ll be looking at how to use this exciting new markup element.

One Attribute, Two Functions

One use of the download attribute is to set a different file download name than the actual link target itself.

<a href="/files/gkridjhf8876ff.pdf" download="Guitar_eBook.pdf">Download my Guitar book</a>

It’s also useful for downloading browser-friendly files like images instead of navigating to the file. You can even keep the original file name by omitting the file name parameter:

<a href="/images/myVacation.jpg" download /a>

Testing for Browser Support

At this time, neither Internet Explorer or Safari support the download attribute. For users of those browsers, you might want to suggest a file name to Save As. The following jQuery code appends downloading instructions beside the download link. Testing for the download attribute is achieved using a [attribute name] in element test. The jQuery get() function is employed to return the underlying <a> element.

$( document ).ready(function() {    
    var downloadDiv = $('#downloadLink');
    downloadDiv.append($('<a>').attr('href', 'rob2014_aff4wws.doc').attr('download', 'Guitar_eBook.doc').text('Download my Guitar book'));
    if (!('download' in downloadDiv.children('a:first').get(0))) {
       downloadDiv.append(' (save the file as "Guitar_eBook.doc")'); 
    }
});

<div id="downloadLink"></div>

Downloading Multiple Files with One Click

It stands to reason that if you can trigger the click event on hyperlinks using JavaScript code, you should be able to download multiple files at once. However, in practice, only Chrome seems to support this.

Such a feature would be useful where you wanted to give the user the option of downloading a whole page of images via a “Download All” link. Here is a simple example with three image links.

<a href="http://www.robgravelle.com/images/35333443.jpg" download="rob1.jpg" class="download_file"><img src="http://www.robgravelle.com/images/35333443.jpg"></a>

<a href="http://www.robgravelle.com/images/765443.jpg" download="rob2.jpg" class="download_file"><img src="http://www.robgravelle.com/images/765443.jpg"></a>

<a href="http://www.robgravelle.com/images/899755444.jpg" download="rob3.jpg" class="download_file"><img src="http://www.robgravelle.com/images/899755444.jpg"></a>
<br><br>
<a id="downloadAll" href="#">Download All Images</a>

Clicking on the last link would invoke the following click() code. Note that the images are retrieved for the purposes of triggering the click events and not the ‘<a>’ tags. It’s easy to forget that the elements within the ‘<a>’ tags are what triggers the link-through action and not the ‘<a>’ element itself.

$(document).ready(function() { 
  $('#downloadAll').click(function() {
      $('a.download_file > img').each(function() {
        $(this).trigger( "click" );
      });

    return false; //cancel navigation
  });
});

The each() call isn’t really necessary because trigger() will be invoked on every element in the queried collection.

$('a.download_file > img').trigger( "click" );

Using a Button Instead of a Link

Just because the download attribute is limited to hyperlinks, that doesn’t mean that we can’t use the above technique to invoke it on other elements, such as a button. In fact, the only thing that would change is the element ID.

$(document).ready(function() { 
  $('#downloadAllButton').click(function() {
    $('a.download_file > img').trigger( "click" );

    return false;
  });
});
<input id="downloadAllButton" type="button" value="Download file" />

A more complex scenario would be a situation where the images are not contained within <a> tags. Now to download the images, we have to dynamically create the <a> tags and copy the image paths to the href attribute. Such is the case in the following HTML, where the images are presented but not contained in links:

<img src="http://farm1.staticflickr.com/191/513636061_98d07f7966_m.jpg"   class="download_file" />
<img src="http://farm6.staticflickr.com/5098/5493205794_6d236910b6_m.jpg" class="download_file" />
<img src="http://farm3.staticflickr.com/2488/4293394187_c0793b58b0_m.jpg" class="download_file" />
<br><br>
<input id="downloadAllButton" type="button" value="Download files" />

Each image is identified as being downloadable by the “download_file” class. Unlike the previous example, the each() is now necessary because several operations need to be performed. The first is to wrap the image in the jQuery object and store it into a variable for later use. The second line is where most of the work takes place. In it, an <a> tag is created dynamically and appended to the document body. The short form of ‘body’ is used instead of the full “document.body” identifier. Appending the link to the DOM is essential because otherwise, the trigger() won’t do anything. However, it will execute if the <a> element has a CSS display value of “none”.

$('#downloadAllButton').click(function() {
    $('img.download_file').each(function() {
      $this = $(this);                      //save the $(this) object
      $('body').append($('<a>')       //append a new '<a>' element to the DOM
        .css('display', 'none')             //hide the new '<a>' element
          .attr('href', $this.attr('src'))  //make the image path the link href
            .attr('download', '')           //keep the file name
              .append($this.clone()))       //copy the image into the new '<a>' element
                .find('img:last()')         //obtain a reference to the copied image
                  .trigger( 'click' );      //fire the click event
                  
      $('a:last()').remove();
    });

    return false;
  });

Conclusion

While not yet supported across all major browsers, the download attribute is already making waves as a much anticipated alternative to server-side PHP-based file downloading solutions. In the meantime, give it a try, but don’t get rid of your backup code just yet.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured