67

Right now I have a canvas and I want to save it as PNG. I can do it with all those fancy complicated file system API, but I don't really like them.

I know if there is a link with download attribute on it:

<a href="img.png" download="output.png">Download</a>

it will download the file if the user clicks on it. Therefore I came up with this:

$("<a>")
    .attr("href", "img.png")
    .attr("download", "output.png")
    .appendTo("body")
    .click()
    .remove();

Demo: http://jsfiddle.net/DerekL/Wx7wn/

However, it doesn't seem to work. Does it have to be trigger by a user action? Or else why didn't it work?

starball
  • 20,030
  • 7
  • 43
  • 238
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247

2 Answers2

93

As @Ian explained, the problem is that jQuery's click() is not the same as the native one.

Therefore, consider using vanilla-js instead of jQuery:

var a = document.createElement('a');
a.href = "img.png";
a.download = "output.png";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);

Demo

Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 1
    Why append to body at all? It seems to work fine without doing so. Example: https://jsfiddle.net/azw08zt5/135/ – Jsilvermist Jun 24 '17 at 13:37
  • 5
    @Jsilvermist Doesn't seem to work on Firefox if I don't insert the link into the tree. – Oriol Apr 02 '18 at 18:58
  • 1
    its working but also open image ,,in my case i have download only – Kapil Soni Sep 24 '20 at 09:10
  • It's working for some images like google images or so, but it doesn't work for images from professional websites like Pixabay or pixels as they require authenticated user so they return 403, however right clicking then save image as doesn't return 403, is there any way to make javascript just do a simple right click with save image as? – Zeyad Shaban May 15 '21 at 10:28
  • Didn't do anything in firefox/linux – chovy Nov 20 '21 at 11:01
  • Poor performance when using base64 > 1MB in src – Rodrigo Vieira Jul 22 '22 at 14:02
84

The problem is that jQuery doesn't trigger the native click event for <a> elements so that navigation doesn't happen (the normal behavior of an <a>), so you need to do that manually. For almost all other scenarios, the native DOM event is triggered (at least attempted to - it's in a try/catch).

To trigger it manually, try:

var a = $("<a>")
    .attr("href", "https://i.stack.imgur.com/L8rHf.png")
    .attr("download", "img.png")
    .appendTo("body");

a[0].click();

a.remove();

DEMO: http://jsfiddle.net/HTggQ/

Relevant line in current jQuery source: https://github.com/jquery/jquery/blob/1.11.1/src/event.js#L332

if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
        jQuery.acceptData( elem ) ) {
drzaus
  • 24,171
  • 16
  • 142
  • 201
Ian
  • 50,146
  • 13
  • 101
  • 111
  • Could this be put inside a .each()? I can only seem to download one image at a time using this technique, probably has something to do with overloading the variable... – bafromca Jun 19 '14 at 18:45
  • 5
    [I don't think you need to attach/remove it from the DOM](http://jsfiddle.net/HTggQ/93/) -- you should just be able to call `$('').attr(...)[0].click()` – drzaus Jul 02 '14 at 14:55
  • 2
    or [even better, no jquery](http://stackoverflow.com/a/16523173/1037948) -- http://jsfiddle.net/HTggQ/95/ – drzaus Jul 02 '14 at 15:17
  • @drzaus The attach/remove from the DOM was from the OP's code, it wasn't something I added. I kept the same code, but added the needed change (`a[0].click()`) which is the whole point. Just as well, the OP was using jQuery, so I kept with what they were using. But good points – Ian Jul 02 '14 at 15:27
  • 3
    @user963936 It looks like since Firefox 20, they only allow same-origin URLs for the `download` attribute (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download). If you inspect the DOM after the script runs, it looks like Firefox converts the tag to an ``, and then jQuery loses its reference to it (why the `.remove()`) doesn't seem to do anything. Changing the `href` to point to a resource on the jsfiddle site seems to work in Firefox. – Ian May 06 '15 at 14:16
  • @user963936 Sorry I couldn't give a solution, only an explanation. I wasn't aware of browser quirks around this, I was just trying to solve the OP's original problem, which was that the `.click()` didn't work. I'm sure there's something that can be done in a different way to accomplish the same feature – Ian May 08 '15 at 13:53
  • I am trying to do this same thing, but when I click the link it downloads the web page that I am on. I inspected the element and the link to the photo is in the download tag of the link. I am just modifying the attribute after the page loads though, does that matter? – Travis Tubbs Sep 09 '16 at 14:55
  • 4
    It's not downloading it, it's just opening in a new window – Zeyad Shaban May 15 '21 at 10:27
  • 3
    This answer is no longer valid as 2022 because browsers have changed and now browsers will ALWAYS follow a link and open up a file it can read. The browsers only trigger a download if it cannot read the resource ie : zip or archive. https://stackoverflow.com/questions/51861852/pdf-file-not-downloading-with-html5-download-attribute – Grogu Apr 17 '22 at 01:52