Opening and closing a new tab when downloading
greasemonkey, javascript, tabs
Solution
There are three basic parts to what you want:
- You must Intercept the download links.
- 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.
- 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:
- 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.
- 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;
}
Problem
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 - I'm doing this in a Greasemonkey script - 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.