Force Download with JavaScript

By  on  

Force download scripts have been an important part of internet usability for a long time.  I can attest to that by the number of times I've implemented this feature on the server side and the popularity of my PHP Force Download post, even to this day.  With the web world having moved much more the client side, I started looking for a method to force download without the need of a server, and I found it....right in the Firefox DevTools Debugger!

The JavaScript

The function to do this is quite small and relies on URL.createObjectUrl:

function downloadFile(data, fileName, type="text/plain") {
  // Create an invisible A element
  const a = document.createElement("a");
  a.style.display = "none";
  document.body.appendChild(a);

  // Set the HREF to a Blob representation of the data to be downloaded
  a.href = window.URL.createObjectURL(
    new Blob([data], { type })
  );

  // Use download attribute to set set desired file name
  a.setAttribute("download", fileName);

  // Trigger the download by simulating click
  a.click();

  // Cleanup
  window.URL.revokeObjectURL(a.href);
  document.body.removeChild(a);
}

The function injects an <a> element into the body, sets it URL to a Blob value to the text content of the destination file, and clicks the element to trigger the download.  The element remains hidden during the process and is removed from the DOM immediately after the click() call.  As soon as the function is called, the browser's download prompt is displayed.

I look forward to learning more about both createObjectURL and Blob; those two are the true magic of this technique!

Shout out to Sneha Jain for implementing this great technique within the Firefox DevTools debugger!

Recent Features

  • By
    5 Ways that CSS and JavaScript Interact That You May Not Know About

    CSS and JavaScript:  the lines seemingly get blurred by each browser release.  They have always done a very different job but in the end they are both front-end technologies so they need do need to work closely.  We have our .js files and our .css, but...

  • By
    Responsive Images: The Ultimate Guide

    Chances are that any Web designers using our Ghostlab browser testing app, which allows seamless testing across all devices simultaneously, will have worked with responsive design in some shape or form. And as today's websites and devices become ever more varied, a plethora of responsive images...

Incredible Demos

  • By
    Full Width Textareas

    Working with textarea widths can be painful if you want the textarea to span 100% width.  Why painful?  Because if the textarea's containing element has padding, your "width:100%" textarea will likely stretch outside of the parent container -- a frustrating prospect to say the least.  Luckily...

  • By
    MooTools 1.2 Tooltips: Customize Your Tips

    I've never met a person that is "ehhhh" about XHTML/javascript tooltips; people seem to love them or hate them. I'm on the love side of things. Tooltips give you a bit more information about something than just the element itself (usually...

Discussion

  1. Pauli Sudarshan Terho

    Typo {type} should be replaced by {type: type}. Ex: {type: "text/plain;charset=utf-8"}

    • { type } is acting exactly like { type : type}

  2. Bronius

    Quite nice and concise. This works with generating a text file and downloading it: What’s the magic to fetch a remote text file not even on my server and handing it back to the browser for download? I want to avoid streaming through php.. In my case they are small enough that maybe I could stream thru js?

    • Bronius

      Ah ha! It is possible! Preceding Dave Walsh’s script above, I first stream a fetch of the file (remote or “local” on the server) with fetch() like:

              let blob = await fetch(uri).then(r => r.blob());
              let fileContents = await (new Response(blob)).text();
      

      Then I pass fileContents into the function in this solution as data. I don’t know if this is the best way, but it seems to be working great in my testing.

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