Javascript / angular: perform asynchronous http from onunload - javascript

I'm working with a web app that locks resources on a server. To unlock them, it has to delete a resource on the server with an HTTP DELETE. I know this isn't reliable and there's a periodic cleanup running as well which unlocks them, but the goal is to unlock the resource as soon as possible.
I cannot change the locking architecture (it's not my system), I just have to make the best stab at unlocking.
One point where I need to unlock is when the tab or browser is closed. First, I'm handling the onbeforeunload and if the document is dirty, prompting the user for confirmation that they want to close:
$window.onbeforeunload = function() {
if (documentIsDirty) {
return "Some prompt text";
}
};
I can't unlock from within onbeforeunload, as the user may choose to cancel the close. But there's no event (correct me if I'm wrong) between onbeforeunload and onunload.
If I try to make the call from in onunload, then the tab/session gets destroyed as soon as the onunload function returns. Trouble is, that's before the http request has completed, and it turns out that the resource doesn't actually get unlocked.
$window.onunload = function() {
$http.delete('/a/lock/url');
// I've tried forcing a digest cycle to try and flush the request
// through, but it never gets sent either way
$rootScope.$digest();
};
Now, I know it's anathema to actually block in Javascript, but it appears that once onload returns, that's all she wrote.
Is there any way to block until the http request has actually completed, and prevent onunload from returning until then?
[UPDATE]
Solution was as below - use XMLHttpRequest synchronously. It's noisily deprecated but does (at the time of writing) still work at least in Chrome.
var request = new XMLHttpRequest();
request.open('DELETE', url, false);
request.setRequestHeader('X-XSRF-TOKEN', myXSRFToken);
request.send();

$http will always do requests asynchronously because internally it's just using XMLHttpRequest and always passing true as the third parameter to a request's open function. From the MDN documentation for XMLHttpRequest's open function:
An optional Boolean parameter, defaulting to true, indicating whether or not to perform the operation asynchronously. If this value is false, the send()method does not return until the response is received.
If you want to do a synchronous request you can just use XMLHttpRequest directly and pass false as the third parameter to open. Since this is when the site is closing it's not really necessary to use Angular's $http anyway.

Recently, Have faced same issue while doing this kinda activity and resolved it by using navigator.sendBeacon() .The navigator.sendBeacon() method asynchronously sends a small amount of data over HTTP to a web server. For latest browser you could do like
window.addEventListener('beforeunload', function (event) {
data = new FormData();
// for CSRF Token
token = $('meta[name="csrf-token"]').attr('content');
data.append("key", value);
data.append("authenticity_token", token);
navigator.sendBeacon("URL", data);
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = 'Are you sure you want to leave this page without saving?';
});
For more details checkout Navigator.sendBeacon() and Window: beforeunload event

Related

How To Capture A Website's Closing Event? [duplicate]

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.

How to know when a page redirect or refresh action is cancelled, Javascript

I am currently working on a page where a preloader is shown to the user onPageNavigating, and when the user cancels the navigation from the browser, I would like to remove the preloader.
What i have tried.
Create a countDown timer
Start timer onPageNavigating
If timer elapse, assume navigation was cancelled
Hide preloader
But the problem with this method is that the time of navigation may vary based on network speed which makes it not feasible for my use-case.
What are other options to implement or is there an available browser API for this? because I can't find anything related via search.
EDIT:
Similar use-case where issue persists:
For example visit:
https://developer.android.com/guide or any link within its domain and notice the horizontal preloader at the top of the page. When you start navigation or refresh from the page you see the preloader shown but the challenge persist as I explained. Cancel the refresh immediately and the preloader would still be visible which isn't meant to be so.
Edit 2:
Maybe rephrasing the question will help.
When i said onPageNavigating i actually mean Javascript's beforeunload event which is triggered when a user clicks on a link or initiates a page refresh.
window.addEventListener('beforeunload', function (e) {
showPreloader();
// ...At this point the preloader is visible
});
Because the beforeunload event can be cancelled. How can you tell when it is cancelled so the preloader can be hidden?
Imaginary event:
window.addEventListener('beforeunloadcancelled', function (e) {
hidePreloader();
// ...This is what i am looking for
});
TL;DR
There is one approach to receive or emulate beforeunloadcancelled event in browsers using only browser APIs right now. But it doesn't work due to bugs in browsers which still haven't been fixed
Below I'll describe the main approach, why it doesn't work and what workarounds may be.
Approach based on browser APIs only
The main idea of emulating beforeunloadcancelled event is to get event that navigation request for next page has been cancelled by the user. You cannot control such kind of requests in the main browser context but, fortunately, you can do so from the service worker context.
If your service worker subscribes to fetch event, you will be able to work with all navigation requests your application makes:
// in Service Worker global scope
self.addEventListener('fetch', evt => {
if (evt.request.mode === 'navigate') {
console.log(`browser is navigating to the ${evt.request.url} resource now`);
}
});
According to the specification each Request object has property signal. This signal represents a signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object.
You can subscribe to the abort event to get notified when request is aborted by the browser or via an AbortController. In that listener you can post message to the parent window with the information that navigation request has been canceled which actually means beforeunloadcancelled event:
// in Service Worker global scope
self.addEventListener('fetch', evt => {
if (evt.request.mode === 'navigate') {
console.log(`browser is navigating to the ${evt.request.url} resource now`);
evt.request.signal.addEventListener('abort', async () => {
// exit early if we don't have an access to the client (e.g. if it's cross-origin)
if (!evt.clientId) return;
// get the client
const client = await self.clients.get(evt.clientId);
// exit early if we don't get the client (e.g., if it's closed)
if (!client) return;
// send a message to the client
client.postMessage({
msg: 'navigation request has been canceled'
});
})
}
});
Unfortunately, there are bugs that those abort events aren't reflected in service worker. Neither Chromium or Firefox have implemented properly sending the AbortSignal through to FetchEvent.request.signal. Please take a look:
https://bugs.chromium.org/p/chromium/issues/detail?id=823697
https://bugzilla.mozilla.org/show_bug.cgi?id=1394102
So this approach doesn't work at the moment.
You can find more information on that topic here:
Abort Controller for Service Worker
https://developers.google.com/web/updates/2017/09/abortable-fetch#in_a_service_worker
https://github.com/w3c/ServiceWorker/issues/1544
https://github.com/w3c/ServiceWorker/issues/1284
Possible workarounds
Detect of cancelled requests on the server side
If you have access to the server, you can detect that the navigation request has been cancelled on the server side.
Just send a notification to the client side in the event the navigation request is aborted. You can use WebSockets to send notifications. To make sure the notification is sent to the correct client some unique cookies based client Id may be used.
Timers based approaches
As you mentioned in the question, one possible workaround is to use some kind of timers:
window.addEventListener('beforeunload', () => {
showPreloader();
setTimeout(() => {
hidePreloader(); // we're assuming navigation has been cancelled
}, TIMEOUT);
});
To make this approach more precise you can choose the TIMEOUT value based on the current page load speed (which can be found in PerformanceNavigationTiming API):
const [initialNavigation] = window.performance.getEntriesByType('navigation');
const TIMEOUT = initialNavigation.duration * TIMEOUT_RATIO;
Track navigation request load duration with service worker
There is a way to track navigation request progress via service worker. You can send notification to the parent window when the navigation request is done. If navigation does not occur immediately after this notification, it means that navigation is effectively canceled and the preloader can be hidden:
// in Service Worker global scope
self.addEventListener('fetch', evt => {
if (evt.request.mode === 'navigate') {
console.log(`browser is navigating to the ${evt.request.url} resource now`);
evt.respondWith(fetch(evt.request).then(response => {
sendHidePreloaderNotification();
return response;
}));
}
});
You can always combine last two options developing more stable approach.

Run a bash script through CGI on closing of browser window [duplicate]

I'm trying to find out when a user left a specified page. There is no problem finding out when he used a link inside the page to navigate away but I kind of need to mark up something like when he closed the window or typed another URL and pressed enter. The second one is not so important but the first one is. So here is the question:
How can I see when a user closed my page (capture window.close event), and then... doesn't really matter (I need to send an AJAX request, but if I can get it to run an alert, I can do the rest).
Updated 2021
TL;DR
Beacon API is the solution to this issue (on almost every browser).
A beacon request is supposed to complete even if the user exits the page.
When should you trigger your Beacon request ?
This will depend on your usecase. If you are looking to catch any user exit, visibilitychange (not unload) is the last event reliably observable by developers in modern browsers.
NB: As long as implementation of visibilitychange is not consistent across browsers, you can detect it via the lifecycle.js library.
# lifecycle.js (1K) for cross-browser compatibility
# https://github.com/GoogleChromeLabs/page-lifecycle
<script defer src="/path/to/lifecycle.js"></script>
<script defer>
lifecycle.addEventListener('statechange', function(event) {
if (event.originalEvent == 'visibilitychange' && event.newState == 'hidden') {
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
}
});
</script>
Details
Beacon requests are supposed to run to completion even if the user leaves the page - switches to another app, etc - without blocking user workflow.
Under the hood, it sends a POST request along with the user credentials (cookies), subject to CORS restrictions.
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
The question is when to send your Beacon request. Especially if you want to wait until the last moment to send session info, app state, analytics, etc.
It used to be common practice to send it during the unload event, but changes to page lifecycle management - driven by mobile UX - killed this approach. Today, most mobile workflows (switching to new tab, switching to the homescreen, switching to another app...) do not trigger the unload event.
If you want to do things when a user exits your app/page, it is now recommended to use the visibilitychange event and check for transitioning from passive to hidden state.
document.addEventListener('visibilitychange', function() {
if (document.visibilityState == 'hidden') {
// send beacon request
}
});
The transition to hidden is often the last state change that's reliably observable by developers (this is especially true on mobile, as users can close tabs or the browser app itself, and the beforeunload, pagehide, and unload events are not fired in those cases).
This means you should treat the hidden state as the likely end to the user's session. In other words, persist any unsaved application state and send any unsent analytics data.
Details of the Page lifecyle API are explained in this article.
However, implementation of the visibilitychange event, as well as the Page lifecycle API is not consistent across browsers.
Until browser implementation catches up, using the lifecycle.js library and page lifecycle best practices seems like a good solution.
# lifecycle.js (1K) for cross-browser compatibility
# https://github.com/GoogleChromeLabs/page-lifecycle
<script defer src="/path/to/lifecycle.js"></script>
<script defer>
lifecycle.addEventListener('statechange', function(event) {
if (event.originalEvent == 'visibilitychange' && event.newState == 'hidden') {
var url = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(url, data);
}
});
</script>
For more numbers about the reliability of vanilla page lifecycle events (without lifecycle.js), there is also this study.
Adblockers
Adblockers seem to have options that block sendBeacon requests.
Cross site requests
Beacon requests are POST requests that include cookies and are subject to CORS spec. More info.
There are unload and beforeunload javascript events, but these are not reliable for an Ajax request (it is not guaranteed that a request initiated in one of these events will reach the server).
Therefore, doing this is highly not recommended, and you should look for an alternative.
If you definitely need this, consider a "ping"-style solution. Send a request every minute basically telling the server "I'm still here". Then, if the server doesn't receive such a request for more than two minutes (you have to take into account latencies etc.), you consider the client offline.
Another solution would be to use unload or beforeunload to do a Sjax request (Synchronous JavaScript And XML), but this is completely not recommended. Doing this will basically freeze the user's browser until the request is complete, which they will not like (even if the request takes little time).
1) If you're looking for a way to work in all browsers, then the safest way is to send a synchronous AJAX to the server. It is is not a good method, but at least make sure that you are not sending too much of data to the server, and the server is fast.
2) You can also use an asynchronous AJAX request, and use ignore_user_abort function on the server (if you're using PHP). However ignore_user_abort depends a lot on server configuration. Make sure you test it well.
3) For modern browsers you should not send an AJAX request. You should use the new navigator.sendBeacon method to send data to the server asynchronously, and without blocking the loading of the next page. Since you're wanting to send data to server before user moves out of the page, you can use this method in a unload event handler.
$(window).on('unload', function() {
var fd = new FormData();
fd.append('ajax_data', 22);
navigator.sendBeacon('ajax.php', fd);
});
There also seems to be a polyfill for sendBeacon. It resorts to sending a synchronous AJAX if method is not natively available.
IMPORTANT FOR MOBILE DEVICES : Please note that unload event handler is not guaranteed to be fired for mobiles. But the visibilitychange event is guaranteed to be fired. So for mobile devices, your data collection code may need a bit of tweaking.
You may refer to my blog article for the code implementation of all the 3 ways.
I also wanted to achieve the same functionality & came across this answer from Felix(it is not guaranteed that a request initiated in one of these events will reach the server).
To make the request reach to the server we tried below code:-
onbeforeunload = function() {
//Your code goes here.
return "";
}
We are using IE browser & now when user closes the browser then he gets the confirmation dialogue because of return ""; & waits for user's confirmation & this waiting time makes the request to reach the server.
Years after posting the question I made a way better implementation including nodejs and socket.io (https://socket.io) (you can use any kind of socket for that matter but that was my personal choice).
Basically I open up a connection with the client, and when it hangs up I just save data / do whatever I need. Obviously this cannot be use to show anything / redirect the client (since you are doing it server side), but is what I actually needed back then.
io.on('connection', function(socket){
socket.on('disconnect', function(){
// Do stuff here
});
});
So... nowadays I think this would be a better (although harder to implement because you need node, socket, etc., but is not that hard; should take like 30 min or so if you do it first time) approach than the unload version.
The selected answer is correct that you can't guarantee that the browser sends the xhr request, but depending on the browser, you can reliably send a request on tab or window close.
Normally, the browser closes before xhr.send() actually executes. Chrome and edge look like they wait for the javascript event loop to empty before closing the window. They also fire the xhr request in a different thread than the javascript event loop. This means that if you can keep the event loop full for long enough, the xhr will successfully fire. For example, I tested sending an xhr request, then counting to 100,000,000. This worked very consistently in both chrome and edge for me. If you're using angularjs, wrapping your call to $http in $apply accomplishes the same thing.
IE seems to be a little different. I don't think IE waits for the event loop to empty, or even for the current stack frame to empty. While it will occasionally correctly send a request, what seems to happen far more often (80%-90% of the time) is that IE will close the window or tab before the xhr request has completely executed, which result in only a partial message being sent. Basically the server receives a post request, but there's no body.
For posterity, here's the code I used attached as the window.onbeforeunload listener function:
var xhr = new XMLHttpRequest();
xhr.open("POST", <your url here>);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var payload = {id: "123456789"};
xhr.send(JSON.stringify(payload));
for(var i = 0; i < 100000000; i++) {}
I tested in:
Chrome 61.0.3163.100
IE 11.608.15063.0CO
Edge 40.15063.0.0
Try this one. I solved this problem in javascript, sending ajax call to server on browse or tab closing. I had a problem with refreshing page because on onbeforeunload function including refreshing of the page. performance.navigation.type == 1 should isolate refresh from closing (on mozzila browser).
$(window).bind('mouseover', (function () { // detecting DOM elements
window.onbeforeunload = null;
}));
$(window).bind('mouseout', (function () { //Detecting event out of DOM
window.onbeforeunload = ConfirmLeave;
}));
function ConfirmLeave() {
if (performance.navigation.type == 1) { //detecting refresh page(doesnt work on every browser)
}
else {
logOutUser();
}
}
$(document).bind('keydown', function (e) { //detecting alt+F4 closing
if (e.altKey && e.keyCode == 115) {
logOutUser();
}
});
function logOutUser() {
$.ajax({
type: "POST",
url: GWA("LogIn/ForcedClosing"), //example controller/method
async: false
});
}
Im agree with Felix idea and I have solved my problem with that solution and now I wanna to clear the Server Side solution:
1.send a request from client side to server
2.save time of the last request recived in a variable
3.check the server time and compare it by the variable of last recived
request
4.if the result is more than the time you expect,start running the
code you want to run when windows closed...
Use:
<body onUnload="javascript:">
It should capture everything except shutting down the browser program.

Asynchronous webRequest.onBeforeRequest URL inspection using Firefox native messages

I'm trying to create URL inspector using Firefox native messages. The problem is, when native application sends a verdict, onBeforeRequest listener already released request, thus redirection doesn't happen.
Can you please help to make my extension wait for reply for up to 2 seconds and redirect the request if answer is "0"?
var port = browser.runtime.connectNative("ping_pong");
function inspectURL(requestDetails) {
console.log("Loading: <" + requestDetails.url + ">");
port.postMessage(requestDetails.url);
console.log("Posting complete <" + requestDetails.url + ">");
}
port.onMessage.addListener((response) = > {
console.log("Received: <" + response + ">");
if (response == "1")
{
console.log("Good url!!!");
}
else
{
console.log("BAD url - redirecting!!!");
return {
redirectUrl: "https://38.media.tumblr.com/tumblr_ldbj01lZiP1qe0eclo1_500.gif"
};
}
});
browser.webRequest.onBeforeRequest.addListener(
inspectURL,
{ urls: ["<all_urls>"] },
["blocking"]
);
Firefox
Firefox supports asynchronous webRequest blocking/modification listeners from Firefox 52 onward. To do so (MDN: webRequest: Modifying requests (and multiple other pages)):
From Firefox 52 onwards, instead of returning BlockingResponse, the listener can return a Promise which is resolved with a BlockingResponse. This enables the listener to process the request asynchronously.
So, in your wrbRequest.onBeforeRequest listener, you return a Promise which you resolve with the BlockingResponse.
You will need to store a list of requests for which you have asked for information from your port. The response from your port needs to uniquely identify the request to which it's responding. Keep in mind that this is all asynchronous, so you can have multiple requests in flight at the same time. You must appropriately track these and only resolve the appropriate Promise. Assuming your responses from your port don't rapidly change, you should store a list of URLs which have been checked (both good and bad), so you can respond immediately to URLs which have already been checked.
Chrome
What you desire is not possible in Chrome. You will need to resolve you issue in some other way.
There's no way at all. It's possible since Firefox 52, see this answer. It's still not possible on Chrome.
Native Messaging is an asynchronous API. After posting a message, you will not receive a reply until you go back to the event loop (e.g. current code terminates).
However, blocking WebRequest API requires a synchronous reply. This is a core limitation, because asynchronous reply may never come or come after an uncertain delay, and the network stack won't wait for that to happen. I mean, it could, but the design of the API deliberately forbids it.
Basically: even if the reply is ready, your code will not receive it until inspectURL terminates, at which point WebRequest already carries on with the request. There is no way in JavaScript to make it synchronous.

Run JavaScript code on window close or page refresh?

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.

Categories

Resources