I have a Flash game that I'm trying to save the state of when the user closes the browser tab. It is using the following jquery code:
//Called from Flash when window closes
function sendRequest(url, params) {
$.ajax({
type: "POST",
async: false,
url: url,
data: params
})
}
$(window).unload(function() {
//Make Flash attempt to save the game when the window closes.
//Flash gets the necessary data and calls sendRequest()
document["flashGame"].saveBeforeUnload();
});
Firefox: Works correctly
Chrome: Works correctly when reloading but not when closing tabs or closing the browser
IE (all versions): Does not work at all
I want it to work in all browsers correctly, but most important is Chrome (not many of our users have IE).
Flash is correctly calling sendRequest (in all browsers, tested with an alert), so I don't believe the problems come from Flash, but it might.
The short answer is that you can't.
The onbeforeunload event was initially introduced by Microsoft to allow a standard confirmation dialog. It is now supported in the original form by most browsers and in most case a short, non interactive, function is allowed to execute (for example you may log your state in the localStorage). But, as described in the cross-browser jQuery API, this is unreliable :
The exact handling of the unload event has varied from version to
version of browsers. For example, some versions of Firefox trigger the
event when a link is followed, but not when the window is closed. In
practical usage, behavior should be tested on all supported browsers,
and contrasted with the proprietary beforeunload event.
As there are many potential security problems related to the execution of a task when the user asked to end the page (and thus the script), this will probably be unreliable until (and if) a serious normalization effort is done to precise what exactly can be done in a onbeforeunload callback.
For the predictable future, it's recommended to not depend on onbeforeunload but to use other schemes, for example constant background saving or a big "save" button.
Try window.onbeforeunload. I found this in the jQueryBug Tracker as a possible solution:
window.onbeforeunload = function() { return "text"; }
Related
Situation
In our Android app (Xamarin), we open a web page using an ActionView intent. The code looks like this:
Intent intent = new Intent((string)Intent.ActionView, Android.Net.Uri.Parse(args.url));
intent.AddFlags(ActivityFlags.NewTask);
The opened page at some point does a JS redirect, with a line like this:
window.location = '...';
We tried many different variations of that line, including window.location.href = '...', window.location.assign('...'); and some more. All show the same behavior.
Problem
This has worked fine for years now, in all browsers - but now we ran into a problem, when the browser on the android device is the Edge browser:
When the browser tab is initially opened by the intent, the window.location = '...' line in Javascript is just ignored by the browser. No error message - just ignored.
However, if that same browser tab with exactly the same URL is opened manually (either by reloading or by copying and pasting the URL), the JS redirect is executed just fine.
Question
How do we fix this, how do we make the JS redirect reliably work?
My guess is that we are running into a security feature, which prevents JS redirects in browser tabs that the user has never interacted with.
Is there something (maybe an intent flag?) to circumvent this? We already tried the flag GrantWriteUriPermission, but it did not help.
Possible duplicates
Android Browser Facebook Redirect Does Not Always Trigger Intent for URL :
The proposed situation of setting the URL on a link and faking a click on it did not work.
Microsoft Edge security
Microsoft Edge recently fixed an issue regarding XSS Targeting Non-Script Elements (June 24, 2021).
The vulnerability was found by two researcher when they visited a website in another language via the Microsoft Edge browser and attempted to translate the page. The goal of the recent fix by Microsoft is to avoid vulnerability regarding accessing dynamically to a content from a third party application and specifically in the case of browser redirection. They need to act quickly because the vulnerability is quite huge.
In order to mitigate a large class of potential cross-site scripting issues, the Microsoft Edge Extension system has incorporated the general concept of Content Security Policy (CSP)
Ok, but ... is there any solution?
Maybe you can find a solution to solve your issue here, in particular the part concerning the <button onclick="...">.
Inline code is considered harmful in concept of CSP and microsoft recommend some good practices :
1 - The clickHandler definition must be moved into an external JavaScript
2 - The inline event handler definitions must be rewritten in terms of addEventListener and extracted into your external js file. If you are currently starting your program using code like <body onload="main();">, consider replacing it by hooking into the DOMContentLoaded event of the document, or the load event of the window, depending on your requirements. Use the former, since it generally triggers more quickly.
3 - Function inside onclick call must be rewritten to avoid converting the string of function into JavaScript for running.
The code exemple of the external .js file cited in the documentation look like this :
function awesome() {
// Do something awesome!
}
function totallyAwesome() {
// do something TOTALLY awesome!
}
function awesomeTask() {
awesome();
totallyAwesome();
}
function clickHandler(e) {
setTimeout(awesomeTask, 1000);
}
function main() {
// Initialization work goes here.
}
// Add event listeners once the DOM has fully loaded by listening for the
// `DOMContentLoaded` event on the document, and adding your listeners to
// specific elements when it triggers.
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button').addEventListener('click',
clickHandler);
main();
});
Hope it's helps
Is there a way to run a final JavaScript code when a user closes a browser window or refreshes the page?
I'm thinking of something similar to onload but more like onclose? Thanks.
I don't like the onbeforeunload method, which always yields to a confirmation box popping up (leave page/ stay on mozilla) or (reload/ don't reload on chrome). Is there a way to execute the code quietly?
There is both window.onbeforeunload and window.onunload, which are used differently depending on the browser. You can assign them either by setting the window properties to functions, or using the .addEventListener:
window.onbeforeunload = function(){
// Do something
}
// OR
window.addEventListener("beforeunload", function(e){
// Do something
}, false);
Usually, onbeforeunload is used if you need to stop the user from leaving the page (ex. the user is working on some unsaved data, so he/she should save before leaving). onunload isn't supported by Opera, as far as I know, but you could always set both.
Ok, I found a working solution for this, it consists of using the beforeunload event and then making the handler return null. This executes the wanted code without a confirmation box popping-up. It goes something like this:
window.onbeforeunload = closingCode;
function closingCode(){
// do something...
return null;
}
Sometimes you may want to let the server know that the user is leaving the page. This is useful, for example, to clean up unsaved images stored temporarily on the server, to mark that user as "offline", or to log when they are done their session.
Historically, you would send an AJAX request in the beforeunload function, however this has two problems. If you send an asynchronous request, there is no guarantee that the request would be executed correctly. If you send a synchronous request, it is more reliable, but the browser would hang until the request has finished. If this is a slow request, this would be a huge inconvenience to the user.
Later came navigator.sendBeacon(). By using the sendBeacon() method, the data is transmitted asynchronously to the web server when the User Agent has an opportunity to do so, without delaying the unload or affecting the performance of the next navigation. This solves all of the problems with submission of analytics data: the data is sent reliably, it's sent asynchronously, and it doesn't impact the loading of the next page.
Unless you are targeting only desktop users, sendBeacon() should not be used with unload or beforeunload since these do not reliably fire on mobile devices. Instead you can listen to the visibilitychange event. This event will fire every time your page is visible and the user switches tabs, switches apps, goes to the home screen, answers a phone call, navigates away from the page, closes the tab, refreshes, etc.
Here is an example of its usage:
document.addEventListener('visibilitychange', function() {
if (document.visibilityState == 'hidden') {
navigator.sendBeacon("/log.php", analyticsData);
}
});
When the user returns to the page, document.visibilityState will change to 'visible', so you can also handle that event as well.
sendBeacon() is supported in:
Edge 14
Firefox 31
Chrome 39
Safari 11.1
Opera 26
iOS Safari 11.4
It is NOT currently supported in:
Internet Explorer
Opera Mini
Here is a polyfill for sendBeacon() in case you need to add support for unsupported browsers. If the method is not available in the browser, it will send a synchronous AJAX request instead.
Update:
It might be worth mentioning that sendBeacon() only sends POST requests. If you need to send a request using any other method, an alternative would be to use the fetch API with the keepalive flag set to true, which causes it to behave the same way as sendBeacon(). Browser support for the fetch API is about the same.
fetch(url, {
method: ...,
body: ...,
headers: ...,
credentials: 'include',
mode: 'no-cors',
keepalive: true,
})
jQuery version:
$(window).unload(function(){
// Do Something
});
Update: jQuery 3:
$(window).on("unload", function(e) {
// Do Something
});
Thanks Garrett
The documentation here encourages listening to the onbeforeunload event and/or adding an event listener on window.
window.addEventListener('beforeunload', function(event) {
//do something here
}, false);
You can also just populate the .onunload or .onbeforeunload properties of window with a function or a function reference.
Though behaviour is not standardized across browsers, the function may return a value that the browser will display when confirming whether to leave the page.
You can use window.onbeforeunload.
window.onbeforeunload = confirmExit;
function confirmExit(){
alert("confirm exit is being called");
return false;
}
The event is called beforeunload, so you can assign a function to window.onbeforeunload.
Is there a way to execute the code quietly? (no popup)
I have used this successfully, where other methods (eg returning null or false) had issues. Tested on ie, Edge, Chrome, Opera.
window.addEventListener('beforeunload', function (e) {
// the absence of a returnValue property on the event will guarantee the browser unload happens
delete e['returnValue'];
// my code that silently runs goes here
});
The above code is pasted directly from Mozilla.org's onbeforeunload doc
Update: This doesn't appear to work on IOS Safari :( So not a total solution, but maybe it still helps someone.
I have an application where a user is allowed to upload and download documents. In the download area, the user can select multiple files (all pdf), and do a merge and download.
Great.
There is a modal window that pops up during the merge process, that shows the status of the merge, with a progress bar using SignalR. This works fine across all browsers, and the download happens.
Problem: There is a Cancel Merge and Download button on this modal. It stops the SignalR hub, and sends an abort() request to the AJAX call. This works in Safari, Chrome, and FF - but not IE.
When I look at Fiddler, Chrome stops the server side method in it's tracks. In IE, it tries to send that request to SignalR. The result in IE is that the modal window goes away as expected, but a few seconds later you receive the prompt that the file has been downloaded (actual processing never stops). Note the difference where the abort is called in the Fiddler session below.
Fiddler (Chrome - working):
Fiddler (IE - not working):
Javascript:
$('#cancelDownloadMerge').on('click', function () {
$.connection.hub.stop();
XX.Documents.DocumentsView.download.abort();
XX.Documents.DocumentsView.mergeCancelled = true;
XX.Documents.DocumentsView._isMerging = false;
});
The AJAX call that gets fired when Merge And Download is initially clicked is lengthy and proprietary, however, cache: false and crossDomain: true (since currently running localhost) are set as such, yet the problem still persists. I do not think the AJAX call is so much relevant as this is very much isolated to IE, but I can try and post more if needed.
I suspect this has something to do with the fact that Chrome and FF are using ServerSentEvents whereas IE is using forever frame (SSE not supported).
I'm stuck on this one, any help is greatly appreciated.
The problem was due to the fact that SignalR was always defaulting to using Forever Frame in IE. Adding the below restriction forced SignalR to use Long Polling in IE.
$.connection.hub.start({ transport: ['webSockets', 'serverSentEvents', 'longPolling'] });
I am updating the url with something like this:
window.history.pushState(null, "Page title", "/?param=" + myParamValue);
This works fine, but when the user hits the back button, the url gets updated but the page does not reload.
I have an ajax routine that updates the content but if possible I don't want to mess with re-implementing back/forward navigation, and I just want the page to reload in case of the user hitting back/forward browser buttons.
Q: Is there a way to force the page to reload the given url on browser back/forward actions from the user?
Bonus Q: also, what if the browser doesn't support window.history.pushState on older browsers? Shall I surround that code in a try/catch block?
if(typeof window.history.pushState === 'function')
//handle your url rewriting
else
//support for older browsers
As for your question about back-button support, the link provided in the comment provides very nice implementations for the feature
You should take a look at this jQuery plugin, it might be useful for you:
http://www.asual.com/jquery/address/
I found it reading this thread:
https://stackoverflow.com/questions/116446/what-is-the-best-back-button-jquery-plugin
I hope it helps.
About the old browser question, I don't think you have to bother with that because you are using ajax, which requires a modern browser too. The main question is: who will visit your web site? If it's mainly people over 40yo, maybe you should bother...
Just my opinion ;)
Edit: Be careful with IE9, it's not handled in it. thx to nbrooks for the info
Of all the browsers, it seems that only Opera doesn't support onunload/onbeforeunload events. (It's been fifteen years now, Opera!) Solutions for this issue have been covered many times, here for example: onbeforeunload support detection
Unfortunately, as of Opera 11.51, ("onbeforeunload" in window) == true, but the actual onbeforeunload event is never executed!
My web application needs to send data to server when a user leaves the page; I'm using a synchronous ajax request for this. It looks like I have to resort to using a "Save" button somewhere on the page to cover up for Opera issues. However, I don't want this button to confuse users whose browsers are capable of auto-saving through ajax, so I'd really like the button to only show up in Opera.
Is my only choice browser-detection? The problem is, Opera has an option to disguise itself as other browsers anyway.
I can't reproduce your finding that 'onbeforeunload' in window is true in Opera 11.5x. This is the best way to do it and should still work. Are you sure you haven't left in some definition somewhere, e.g. you've written
onbeforeunload = function (){ ... }
later in the same script that does the feature detection? If you do alert(window.onbeforeunload), what do you see? Could you share a link to the page with the problem?
Opera screwed the pooch on this one. I detect for Opera by looking for window.opera and if it exists, I deny Opera what it can't handle.
Using unload is no good I think, because it occurs too late in the game. Sometimes onbeforeunload is the only thing that'll do the trick. Once again, I just look for opera on the window object, and, if it exists, deny it the things it can't do. :)
PPK talks about it here: http://www.quirksmode.org/js/detect.html
For anyone stumbling across this post, this is a code snippet I use for detecting onbeforeunload support and if the browser doesn't support it I switch to onunload (note the use of jquery, obviously not required). In my case I use this code (and a little extra) to check if any AJAX requests are still active and stop the user navigating away. Keep in mind that using onunload isn't ideal because the browser will still navigate away from the page but at least it gives you a chance to warn the user that data might have been lost and they should go back and check.
You'll notice I'm using the isEventSupported() function available at https://github.com/kangax/iseventsupported for cross browser support detecting available events.
// If the browser supports 'onbeforeunload'
if (isEventSupported('beforeunload', window)) {
$(window).on('beforeunload', function(){
return 'This page is still sending or receiving data from our server, if you recently submitted data on this page please wait a little longer before leaving.';
});
}
// The browser doesn't support 'onbeforeunload' (Such as Opera), do the next best thing 'onunload'.
else {
$(window).on('unload', function(){
alert('This page was still sending or receiving data from our server when you navigated away from it, we would recommend navigating back to the page and ensure your data was submitted.');
});
}
See my answer to a similar / duplicated question. Basically, it sets up detection on the very first page on your domain and stores that detection result for all subsequent pages in localStorage. Including working example code.