toby88
toby88

Reputation: 115

JavaScript click() method only works once in Chrome extension

I'm trying to download multiple files in a Chrome extension. The following code creates a dummy link to a file, then triggers the .click() event which downloads the file. The problem is that only the first .click() event triggers a download. Subsequent .click() events are ignored.

Here the manifest.json:

{
  "name": "Simple File Downloader",
  "version": "0.1",
  "permissions": ["contextMenus", "http://*/"],
  "background": {
    "persistent": false,
    "scripts": ["sample.js"]
  },
  "content_security_policy": "script-src 'self'; object-src 'self'",
  "manifest_version": 2
}

Here the sample.js:

function onClickHandler(info, tab) {
    var a = document.createElement('a');
    a.href = 'http://or.cdn.sstatic.net/chat/so.mp3';
    a.download = 'so.mp3';
    document.body.appendChild(a);
    a.click(); // this click triggers the download
    // this timeout violates content security policy
    // setTimeout(a, 300); 
    a.click(); // this click doesn't do anything
    document.body.removeChild(a);

    a = document.createElement('a');
    a.href = 'http://or.cdn.sstatic.net/chat/so.mp3';
    a.download = 'so.mp3'; 
    document.body.appendChild(a);
    a.click(); // this click doesn't do anything either
    document.body.removeChild(a);
};

chrome.contextMenus.onClicked.addListener(onClickHandler);
chrome.runtime.onInstalled.addListener(function() {
  chrome.contextMenus.create({"title": "Download File", "id":"download_file"});
});

I've tried:

Surprised why it's so hard to simply save multiple files. Appreciate any help.

Upvotes: 6

Views: 3760

Answers (2)

zertosh
zertosh

Reputation: 5381

The trick is not to use the element.click method but rather to create multiple MouseEvent. For this to work, you'd need to create one MouseEvent for each time you need a click.

function clicker(el, clickCount) {
  var mousedownEvent;
  while(clickCount--) {
    mousedownEvent = document.createEvent("MouseEvent");
    mousedownEvent.initMouseEvent("click", true, true, window, 0, null, null, null, null, false , false, false, false, 0, null);
    el.dispatchEvent(mousedownEvent);
  }
}

clicker(a, 3);
// your anchor 'a' gets clicked on 3 times.

When using this method in Chrome though, you get a warning from the browser asking "This site is attempting to download multiple files. Do you want to allow this? [Deny] [Allow]". So, if you do this inside an extension's background page, the background page receives the warning, the user can't see it, so the user has no way to click "Allow".

A (gross/nasty) workaround is to create a tab that "clicks" the anchor. Something like this:

function _anchorDownloader(url, filename) {
  var timeout = 500;
  return 'javascript:\'<!doctype html><html>'+
    '<head></head>' +
    '<script>' +
      'function initDownload() {'+
        'var el = document.getElementById("anchor");'+
        'el.click();' +
        'setTimeout(function() { window.close(); }, ' + timeout + ');' +
      '}'+
    '</script>' +
    '<body onload="initDownload()">' +
      '<a id="anchor" href="' + url + '" download="'+ filename + '"></a>'+
    '</body>' +
    '</html>\'';
};

function downloadResource(info, tab) {
  // ...
  chrome.tabs.create( { 'url' : _anchorDownloader( url, filename ), 'active' : false  } );
  // ...
}

chrome.contextMenus.create({"title": "Save Image…", "contexts":["image"], "onclick": downloadResource });

For this to work, the extension has to have "tabs" as a permission in the manifest.json. You can tweak the timeout to close the tab, however, if you close it too fast then no download will happen.

Upvotes: 5

Loourr
Loourr

Reputation: 5125

Instead of using the .live() methode which is no longer recommended try .on()

$(document).on("click", "a", function( event ){
    // do whatever
});

here is the documentation

Upvotes: 0

Related Questions