Download binary without triggering onbeforeunload - javascript

I want to kick off a file download for a user when he clicks a link, but I have an onbeforeunload handler that I don't want to get invoked when the download begins. To downloads, I currently have an <a> with the href set to the file location but clicking it results in onbeforeunload being invoked in Chrome (not in FF, though).
I know I can set a private flag and check that in the onbeforeunload handler, but is there some way to kick off the download using ajax? I still want the user to see the usual dialogs when they download the file (Open/Save etc).
Ideas?

The best way to do this is indeed by constructing an iframe and triggering the download from there.
I have tested this in Chrome, IE6+ and Firefox and this approach seems to work in all of them.
Example code:
function DownloadFile(filePath) {
var downloadIframe = $('<iframe />',
{
id : 'downloadIframe'
}).appendTo('body');
downloadIframe.attr('src', filePath);
}
This will only work properly for a one off download (as we've hard coded an id), if you are triggering multiple downloads, then I suggest you reuse the iframe by storing it in a more widely accessible variable.

Add the download attribute to the a tag, which seems to prevent onbeforeunload from firing:
<a download href="mysite.com"></a>
From this answer.

I would guess using the target attribute on the link could do the trick.
download
Will not validate (unless using frameset doctype) but it might work.. It will create a new tab or window and then pop a file download (if the http header says it should), but it might leave the new tabs/windows open, or it might close them after saving...
On the other hand I think having a onbeforeunload handler that you sometimes do not want to trigger sounds suspicious, why do you need that anyway?

I know this answer is really late, but there's a simple solution. Create an iframe dynamically, and set the "src" of it to your download url via JavaScript. This'll kick off the download without triggering the unload event (I think).

The download attribute answer worked well for me, but before I noticed it, I tried this, which also works. Just in case it's useful in some other cases that aren't download links.
var linksThatDisableWarning = $('a'); // all links
linksThatDisableWarning.on('mousedown', () =>
window.isclicking = true);
linksThatDisableWarning.on('mouseup', () =>
setTimeout(() => window.isclicking = false, 200))
window.addEventListener('beforeunload', (event) => {
if (window.isclicking) return;
event.preventDefault();
event.returnValue = '';
});

Related

A way to determine file is loaded in iframe

I've got a problem: I would like to catch a moment when the Save File dialog is closed or my csv file generated on the server is loaded (to hide a spinner for loading). I know there is no such event in the Javascript. I definitely don't want to add cookies for such a minor issue on the backend so I would like to implement another workaround. The only way out I see is iframe but as far as I can see event listeners like onload doesn't work in case of attachment header in Chrome at least. I've also tried to implement a kind of timer checking for iframe status but it worked right after request for file was sent. File is generated on server in some seconds (10-20) so this solution doesn't fit my goals.
I'm using Angular on the frontend so any solution compatible (vanilla JS, jQuery, Angular itself) will be a great help for me. Hope to get any. Thank you, guys!
There is no DOM event, which am aware of here, which is fired when Save file UI dialog is opened or closed.
You can try utilizing focus event to catch when the Save file dialog is closed and window regains focus after calling .click() on <a> element having download attribute set, though the approach is not completely reliable.
// append or display spinner element here
var csvUrl = document.createElement("a");
csvUrl.href = url;
csvUrl.download = "csv_filename";
csvUrl.click();
window.onfocus = function () {
document.body.removeChild(csvUrl);
// remove or hide spinner element here
window.onfocus = null;
}

Read iframe redirect (same domain)

I'm working in writing a chrome extension, and as a result I have the peculiar situation of using non-cross domain Iframes, without the ability to alter the page being displayed in the frame.
When a user clicks a certain link, in he iframe, I want to run some JavaScript. The default behavior for clicking that link is to load page targetpage.com. I don't think it's possible, or easy, to read listen for a particular element being clicked inside an iframe
As a workaround, I figure I can check if the iframe reloads, pointing to targetpage.com, and then perform the action.
Note: the action is entirely in the parent page, let's imagine I'm just trying to trigger an alert box.
I know how to trigger JavaScript when an iframe loads. My three questions are:
1) how can I check the url of an iframe?
2) is there a way to check the iframe, url prior to targetpage.com being loaded. Esther than after?
3) is there a better solution?
You could listen to webNavigation.onBeforeNavigate in background page, it fires when a navigation is about to occur, and you could get url and frameId in the callback parameter.
This may not be the best answer because I haven't played around with this much.
Chrome has a webNavigation API that looks to be something which may come in handy for your extension.
However if you want to get the current domain you're on you'd use...
document.domain
If you're in a subdirectory of that domain you can grab that with...
window.location
It also works with an added hash to the url.
If you want the url without the hash you could use document.referrer or if you feel hacky you could do something like...
var str = window.location
var res = str.toString().split(str.hash)
document.body.innerHTML = res

onhashchange not working within in-app browsers (Facebook, Twiiter)

I'm using the onhashchange window event to detect the url hash change for my single page webapp. This enables me to fire AJAX, while retaining the browser history.
User clicks anchor with href="#hashlink".
onhashchange detects the URL updating.
#hashlink is extracted and passed in as AJAX url (/partials/hashlink.php).
I have discovered an issue. You may already be aware, but Facebook and Twitter have started launching external links within an in-app browser. It seems to prevent the default action of page anchors href, which has killed my hash change detection. Thus my webapp is pretty much useless :-(
The in-app browser for Facebook and Twitter were only released very recently, so finding a solution is proving to be difficult.
Thanks in advance!
I know this is an old question, but I ran into the same problem yesterday. Was not able to find a solution that allowed me to keep my use of hash links and the onhashchange event. I rewrote my code to use the History API, which is widely supported (caniuse says it works for every browser except Opera Mini, but when I tested, it worked for me there, too).
Step-by-step:
turn all hash links into buttons, or some other accessible format (div with a role="link" attribute, etc). (don't forget to include an aria-label attribute if the buttons don't contain text clearly stating their function).
<button class="nav__list-item" data-id="${p.websafeHandle}" aria-label="Read our Q & A with ${p.name}">
<div class="nav__list-item__img" style="background-image: url(${p.thumbnail})"></div>
<span class="nav__list-item__handle">${p.handle}</span>
</button>
add click event listener
const qaLinks = document.querySelectorAll('.nav__list-item')
qaLinks.forEach(link => {
link.addEventListener('click', (e) => this.loadQA(e.currentTarget.dataset.id, true))
})
click event handler to update page content and browser history
loadQA(person, updateHistory) {
// I'm condensing/summarizing the parts of my code that deal with fetching data and
// outputting it on the page, because it's not particularly relevant to this question.
const data = this.data[person]
this.container.innerHTML = template(Templates.qaPage, data.qa)
// the loadQA function will also be called if somebody comes to this page
// with a hash link like the one below. in that case (on an initial visit,
// instead of navigation within my single-page app), I wouldn't want to add
// the page to the history again, thereby creating a double entry,
// which is why I have the updateHistory param
if (updateHistory) {
// update our page title and our URL and add it to the browser history
const title = `MTI - ${data.info.name}`
const hash = `#/qa/${data.info.websafeHandle}`
history.pushState(null, title, hash)
}
}
event handler that will fire whenever someone uses their browser's forward/back buttons
window.addEventListener('popstate', () => this.loadPage())
in the popstate event handler (which is also run on initial page load), get the URL hash and load the page accordingly
loadPage() {
if (!window.location.hash) {
// the param here is for updateHistory
this.showLanding(false)
} else {
const person = this.getHashParam()
person ? this.loadQA(person, false) : this.showLanding(true)
}
}
Side note: I found this app to be really helpful for testing my local code in Facebook's IAB. You give it a link (e.g. to your dev site), it generates a link (you have to use the xip.io one to be able to open it in FB), gives you a QR code to scan with your phone, which connects your phone to its dev tools and opens that link in your browser. then you can copy the link, post it in a private FB post only you can see, and voila! you can visit your local server in the Facebook browser, and Ghostlab gives you access to all the dev tools you'd normally have in Chrome.

Simulating a link click in JQuery with Chrome - using both href and download

I have a Chrome extension that dynamically creates links and adds them to a webpage. The links download files. They each have a download attribute to name the files correctly (only works in Chrome, afaik - but it only needs to), as well as href for the url of the file. How can I simulate clicking one of these links?
I don't need to click the version within the document necessarily, just the link object.
Basically, how can I write, in Javascript/JQuery, code to have Chrome download a file named with download at the href location?
The JQuery click() command didn't seem to do anything, though I can't figure out why.
I know this thread is old, but it's the first result in search, so for others who seeks answer, here it is:
$(css_selector)[0].click();
And you're doing it $(function(){ HERE }); and some js functionality is added to css_selector on original page load, then maybe you want to delay HERE execution by setting timeout.
you can try simulating the click
like this
$("your_selector").trigger("click");
or like this
$("your_selector").click();

How can I change the current URL?

I have the following code that changes the pages from within JavaScript:
var newUrl = [some code to build up URL string];
window.location.replace(newUrl);
But it doesn't change the top URL, so when someone clicks the back button it doesn't go back to the previous page.
How can I have JavaScript change the top URL as well so the browser back button works.
document.location.href = newUrl;
https://developer.mozilla.org/en-US/docs/Web/API/document.location
Simple assigning to window.location or window.location.href should be fine:
window.location = newUrl;
However, your new URL will cause the browser to load the new page, but it sounds like you'd like to modify the URL without leaving the current page. You have two options for this:
Use the URL hash. For example, you can go from example.com to example.com#foo without loading a new page. You can simply set window.location.hash to make this easy. Then, you should listen to the HTML5 hashchange event, which will be fired when the user presses the back button. This is not supported in older versions of IE, but check out jQuery BBQ, which makes this work in all browsers.
You could use HTML5 History to modify the path without reloading the page. This will allow you to change from example.com/foo to example.com/bar. Using this is easy:
window.history.pushState("example.com/foo");
When the user presses "back", you'll receive the window's popstate event, which you can easily listen to (jQuery):
$(window).bind("popstate", function(e) { alert("location changed"); });
Unfortunately, this is only supported in very modern browsers, like Chrome, Safari, and the Firefox 4 beta.
If you just want to update the relative path you can also do
window.location.pathname = '/relative-link'
"http://domain.com" -> "http://domain.com/relative-link"
Hmm, I would use
window.location = 'http://localhost/index.html#?options=go_here';
I'm not exactly sure if that is what you mean.
This will do it:
window.history.pushState(null,null,'https://www.google.com');
<script>
var url= "http://www.google.com";
window.location = url;
</script>
The best way to redirect the user to another URL is by using window.location.assign (See https://developer.mozilla.org/en-US/docs/Web/API/Location/assign).
Nevertheless, if what you want is to change the URL of the page without redirecting the user, then you may use the window.history.replaceState function (See https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState). A combination of this and window.history.pushState is what Single Page Applications (SPAs) use nowadays to keep track of the user navigating throughout the application so that the back button works as expected
You can take a look at the examples in the documentation links I provided to give you an idea on the usage of these functions

Categories