15

On many websites (Dropbox being a good example), when you click on a document to download it, it opens a new window/tab, then the download prompt appears, and the tab/window immediately closes itself (while the prompt remains open).

How do I replicate this behavior using javascript?

I think one approach would be to detect the appearance of that download prompt, then use window.close(). However, I'm not sure how to detect that particular prompt.

A cross-browser solution is preferred, but anything that'll work in Firefox is greatly appreciated.

Clarifications

  1. I'm doing this in a Greasemonkey script
  2. The "link" that opens in the new tab is not necessarily a direct link to the document. Sometimes it is a simple page that starts the download in the background. I'm referring to the type of sites that have their special "download" page...the ones that say something like "your download will begin shortly, if it doesn't start, please click here".

More clarification: With regards to the type of website mentioned in clarification 2 above, what I want to do is click on the download link, have that particular download page load in a new window, and have the window close once the download has been initiated.

Brock Adams
  • 90,639
  • 22
  • 233
  • 295
skittleys
  • 353
  • 1
  • 3
  • 11
  • So, unless I'm forgetting, they use `window.open`, point it to a URL (what to download), and the HTTP headers in the response are specific for it to be downloaded by the user. As soon as the response hits the browser, it automatically closes itself - you don't have to do something. I'll see if I can find an example I've used – Ian Dec 04 '12 at 03:23
  • Are you using a specific web server? – Ian Dec 04 '12 at 03:24
  • For example, you need to use something like this - http://stackoverflow.com/questions/8897458/asp-net-download-file-to-client-browser - depending on your web server – Ian Dec 04 '12 at 03:35
  • I'm not totally sure what you're asking...do you mean what site I'm trying to implement this on? I'm actually just doing a small Greasemonkey script, and there's several different sites I'd like to implement this behaviour on. – skittleys Dec 04 '12 at 03:36
  • Well where are you storing the files you want to allow users to download? You have to store the files somewhere, and then make them available to the user. Making them available isn't enough - you have to serve the content over HTTP and set the appropriate headers to make it behave the way you want. I guess I'm not sure how greasemonkey works – Ian Dec 04 '12 at 03:40
  • Greasemonkey allows users to make changes to how a website behaves or appears on their end. Also, I am the downloader, not the uploader.... See my clarification in the original question. – skittleys Dec 04 '12 at 03:46
  • can't you set an event handler on the secondary window for when the location changes ? – technosaurus Dec 04 '12 at 04:12
  • @technosaurus: _will_ the location change? The actual download is being initiated dynamically in the background, but the actual site being displayed remains the same. – skittleys Dec 04 '12 at 05:44
  • it would depend on the site, some change, others initiate a new tab/window, so you'd have to set up logic for each – technosaurus Dec 04 '12 at 05:59
  • @technosaurus: OK, but what about the sites that don't do either of those? A good example: try to download something from CNET, like [TextPad](http://download.cnet.com/TextPad/3000-2352_4-10002673.html). Use the "direct download link". FYI, I'm using TextPad as a suggestion because the resulting download is a zip, not an exe, so it uses the same download prompt as documents, not executables. Don't know if that actually matters.... – skittleys Dec 04 '12 at 06:39
  • Per your TextPad example, there's no way for the webpage to determine when to close the window or whatever. The way you usually see it, when the new window pops up and the download begins, the browser understands it was an attachment to download (because of the HTTP headers) so it closes the window since it serves no purpose (I'm guessing). The way CNET does it is with ` – Ian Dec 04 '12 at 19:42
  • Actually, I'm pretty sure the HTTP headers still have to be set, because an iframe acts no different than a window in this sense...where normally things are displayed in the browser (if possible, like HTML, JS, CSS files). So for those kinds of files, you need a special HTTP header setting – Ian Dec 04 '12 at 19:46
  • @Ian I'm quite sure that's well outside GreaseMonkey's capabilities. Oh well. It was worth a try! – skittleys Dec 05 '12 at 06:59
  • related: https://stackoverflow.com/q/4197955 – djvg Nov 02 '22 at 14:57

3 Answers3

13

There are three basic parts to what you want:

  1. You must Intercept the download links.
  2. You must send the link to a new tab when it is clicked, and not just modify it with target="_blank". The tab must be opened with javascript, so that javascript will be allowed to close it when the time comes.
  3. Your script must also run-on / handle the "popup" tab, so that it can detect when to close the popup.

For this discussion, refer to this test page at jsFiddle. It is structured like this:

<div id="downloadLinks">
  <ul>
    <li><a class="dwnPageLink" href="http://fiddle.jshell.net/cDTKj/show/">
            Test file, download page at jsFiddle
        </a>
    </li>
    <li><a class="dwnPageLink" href="http://dw.com.com/redir...">
            TextPad (a great text editor), download page at CNET / Download
        </a>
    </li>
  </ul>
</div>

where the a.dwnPageLink links each open a "Download page" -- which automatically starts a file download after a short delay.


Intercept the download links (a.dwnPageLink):

We intercept the links like so:

$("#downloadLinks a.dwnPageLink").each (interceptLink);

function interceptLink (index, node) {
    var jNode   = $(node);
    jNode.click (openInNewTab);
    jNode.addClass ("intercepted");
}

Note that we also add a CSS class, so that we may quickly see which links have been affected.
openInNewTab will be detailed below. It must both open a tab, and it must stop the normal link action.


Send the link to a new tab:

We must use window.open() to handle the link. If the page is not opened via window.open, our script will not be able to close it.

Note that GM_openInTab() cannot be used because it does not cause window.opener to be set properly and otherwise does not provide a mechanism to close the opened tab.

The new tab is launched in openInNewTab, which looks like this:

function openInNewTab (zEvent) {
    //-- Optionally adjust the href here, if needed.
    var targURL     = this.href;
    var newTab      = window.open (targURL, "_blank");

    //--- Stop the link from doing anything else.
    zEvent.preventDefault ();
    zEvent.stopPropagation ();
    return false;
}


Handle the "popup" tab:

It is not possible to monitor for the File dialog from the launching page. So we must set the script to also run on the "popup" tab. Add @include directives accordingly.

The popup portion of our script can detect the File dialog by monitoring the beforeunload event. Browsers will fire the beforeunload event, just before opening the File dialog (and also just before the tab closes, but we can ignore that).

However, we cannot just close the tab when the dialog appears. Doing so will close the dialog too. So we add a small time delay, and a "Confirm" dialog so that the tab will stay open until the File dialog is closed. To clear the Confirm dialog, just hit Enter an extra time (or Click OK).

The code looks like this:

$(window).bind ("beforeunload",  function (zEvent) {
    //-- Allow time for the file dialog to actually open.
    setTimeout ( function () {
            /*-- Since the time it takes for the user to respond
                to the File dialog can vary radically, use a confirm
                to keep the File dialog open long enough for the user 
                to act.
            */
            var doClose = confirm ("Close this window?");
            if (doClose) {
                window.close ();
            }
        },
        444 // 0.444 seconds
    );
} );


Note:

  1. Since the script will be running on both "List" pages and "Download" pages, we can tell which is which by checking window.opener. On a page opened by javascript, this will have a non-null value.
  2. This question did not ask about having the tab load in the background. That can be done, with varying degrees of success, but is a more involved. Ask a new question for that.


Complete script:

This script works on the test page and on CNET / Download pages:

// ==UserScript==
// @name        _Download page, auto closer
// @namespace   _pc
// ******** Includes for "List pages" that have the links we might click...
// @include     http://YOUR_SERVER.COM/YOUR_LIST-PAGE_PATH/*
// @include     http://jsbin.com/ozofom/*
// @include     http://fiddle.jshell.net/qy3NP/*
// @include     http://download.cnet.com/*
// ******** Includes for "Popup pages" that do the actual downloads...
// @include     http://YOUR_SERVER.COM/YOUR_POPUP-PAGE_PATH/*
// @include     http://fiddle.jshell.net/cDTKj/*
// @include     http://dw.com.com/redir?*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @grant       GM_addStyle
// @grant       GM_openInTab
// ==/UserScript==
/*- Important: The @include or @match directives must for for both the pages
    that list the download links, AND the pages that do the actual downloading.

    The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/

var bPageNotOpenedByJavascript = window.opener ? false : true;
if (bPageNotOpenedByJavascript) {
    /***** "Normal" page, which might contain links to special download pages.
    */
    //--- Intercept links to the download pages:
    // For our jsFiddle Test page:
    $("#downloadLinks a.dwnPageLink").each (interceptLink);

    // For CNET/Download:
    $("#downloadLinks div.dlLinkWrapper a").each (interceptLink);

    GM_addStyle ( "                                 \
        a.intercepted {                             \
            background:         lime;               \
        }                                           \
    " );
}
else {
    /***** Page opened by JS in either a popup or new tab.
        This was *most likely* done by us, using window.open.
    */
    $(window).bind ("beforeunload",  function (zEvent) {
        //-- Allow time for the file dialog to actually open.
        setTimeout ( function () {
                /*-- Since the time it takes for the user to respond
                    to the File dialog can vary radically, use a confirm
                    to keep the File dialog open long enough for the user
                    to act.
                */
                var doClose = confirm ("Close this window?");
                if (doClose) {
                    window.close ();
                }
            },
            444 // 0.444 seconds
        );
    } );
}

function interceptLink (index, node) {
    var jNode   = $(node);
    jNode.click (openInNewTab);
    jNode.addClass ("intercepted");
}

function openInNewTab (zEvent) {
    //-- Optionally adjust the href here, if needed.
    var targURL     = this.href;
    var newTab      = window.open (targURL, "_blank");

    //--- Stop the link from doing anything else.
    zEvent.preventDefault ();
    zEvent.stopPropagation ();
    return false;
}
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
9

You can simply do using <a> tag by setting target="_blank"

<a href="http://jqueryui.com/resources/download/jquery-ui-1.9.2.custom.zip" target="_blank">Download</a>​

Demo: http://jsfiddle.net/g5Gn5/

It will open a new window/tab and close automatically once file dialog appears.

Muthu Kumaran
  • 17,682
  • 5
  • 47
  • 70
  • 1
    That's only because the response has a header set to download the item (or because it's a Zipped file). Normally, it would display its contents in the browser. `_blank` just opens it in a new window/tab, but has no effect on how it is opened – Ian Dec 04 '12 at 03:32
  • yes, it's a zipped file and my demo works. Thanks for the downvote. – Muthu Kumaran Dec 04 '12 at 03:39
  • 1
    Haha well just because it works on one file doesn't mean it's a universal solution. As I've pointed out, you can't just use `_blank`...just try using http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js – Ian Dec 04 '12 at 03:41
  • @Ian I can't provide complete solution. You need to set `header` to download `js, html, txt` or any text format files. – Muthu Kumaran Dec 04 '12 at 03:43
  • @Ian Here is the simple solution to download `js` files, `Download` but note this will never works on IE. – Muthu Kumaran Dec 04 '12 at 03:46
  • 1
    This solution won't work for me. See my clarification in the original question. – skittleys Dec 04 '12 at 03:48
  • Only Chrome seems to support that, albeit a useful feature – Ian Dec 04 '12 at 04:09
1

just use this simple solution :

<a href="linkToDownloadFile.html" onclick="download('linkToDownloadFile.html'); return false;">

<script>
    function download(link){
      var popout = window.open(link);
      window.setTimeout(function(){
         popout.close();
      }, 2000);
    }
</script>
APB
  • 307
  • 1
  • 2
  • 8