170

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?

Peter
  • 7,020
  • 4
  • 31
  • 51
  • 1
    Possible duplicate: http://stackoverflow.com/questions/805463/javascript-to-check-when-the-browser-window-is-close – tozlu Nov 18 '12 at 19:08
  • Possible duplicate: http://stackoverflow.com/questions/1631959/browser-window-close-event – Horen Nov 18 '12 at 19:09
  • 29
    Wait- this actually ISN'T a duplicate... He wants to know how to execute something WITHOUT a prompt to the user- the linked questions ask the opposite... – Phildo Nov 20 '13 at 20:37

8 Answers8

135

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
});
   

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), but if you don't return a string or set event.returnValue, then it'll just run the code without showing a message.

Note: The beforeunload event doesn't work from within iframes if the iframe is deleted by its parent (e.g. with iframeElement.remove()), but the unload and pagehide events do work for this case in Chrome. However, as of August 2023 Firefox has a bug which causes these events to not work for the iframe deletion case, meaning it's not currently possible to run code right before an iframe is deleted by its parent using events like unload in Firefox.

joe
  • 3,752
  • 1
  • 32
  • 41
JCOC611
  • 19,111
  • 14
  • 69
  • 90
  • This worked for me: Firefox (69.0.2) and Chrome (77.0.3865.90) – FelipeCaparelli Oct 08 '19 at 10:34
  • 1
    I know this is an accepted answer but when I add an alert inside //DO SOMETHING, it is not sending an alert message. Is this normal? Since I feel it is not running that alert when I close nor when I refresh. – rod james Jun 30 '21 at 02:33
76

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;
}
Ferdinando
  • 964
  • 1
  • 12
  • 23
Peter
  • 7,020
  • 4
  • 31
  • 51
  • 13
    Isn't this fired also when navigating out and when refreshing (F5) ? If so, it does not really address the question... – Jago Jul 26 '13 at 07:27
  • 1
    Not tested, but I think `return false;` does the same (i.e. prevents the default behavior) and it is more semantically correct. – collimarco Sep 24 '13 at 15:12
  • 2
    return false will still pop up the dialog box asking if you want to leave the page or not. I tested it on an anchor. With return null the dialog didn't pop up but it still did the console.log – Ricky Stam Oct 31 '13 at 08:01
  • Just an FYI if others find this, this doesn't work with React as it seems to be triggered by renders. Use this instead: https://www.npmjs.com/package/react-beforeunload – MonsterBasket Dec 09 '22 at 02:54
62

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,
})
Mike
  • 23,542
  • 14
  • 76
  • 87
  • This question was in the scope of a "To send a request to the server" case. The idea was to be able to keep tabs on open tabs per user and clean-up on backend when a tab is closed. – Peter Sep 21 '18 at 17:37
  • @Peter I was including it for completeness, but I deleted it. – Mike Sep 21 '18 at 22:37
  • 2
    @Mike, please, do NOT delete your answer. It not only complete the selected best answer (it should be yours) but also presents the event used to catch window exit or close – Alex8752 Mar 23 '19 at 19:36
  • @Alex8752 I didn't delete my answer, I edited out a portion of it that was irrelevant to the question, which you can view [here](https://stackoverflow.com/posts/52412486/revisions). – Mike Mar 24 '19 at 05:29
  • But sendBeacon() method makes a POST request which gets forbidden at server end due to csrf token missing. – Amandeep Singh May 15 '19 at 17:25
  • 2
    @AshokKumar You're in control of what you send to the server. If you want to send things by GET, there's nothing that prevents you from sending your request to something like `http://example.com/script.php?token=something&var1=val1&var2=val2` thus putting those values into GET. – Mike May 15 '19 at 22:36
  • @Mike Thanks sir i will look into this. – Amandeep Singh May 16 '19 at 09:44
  • Isn't using `navigator.sendBeacon()` on `unload` exactly what we're not supposed to do...? *"It’s intended to be used in combination with the `visibilitychange` event (but not with the `unload` and `beforeunload` events)."* – cYrus Oct 06 '20 at 17:59
  • 2
    @cYrus, Good catch. At the time of posting my answer, MDN [recommended using unload](https://wiki.developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon$revision/1375588), but they have since changed their stance. I've updated my answer accordingly. – Mike Oct 07 '20 at 06:31
  • you are a genius, keepalive: true just saved me hours having to rewrite an existing PUT API to POST to accomodate sendBeacon – danday74 Jan 18 '21 at 22:51
  • this works perfectly. however, im wondering if fetch is strictly required? for example, in an Angular framework environment could you just use the built in HTTP client and ensure that you send keepalive: true in the request (if thats even possible)? or is this somehow restricted to fetch only (ignoring the sendBeacon alternative) – danday74 Jan 18 '21 at 23:51
  • @danday74 To be honest, I have never used Angular, so I wouldn't know. Possibly they even use the fetch API behind the scenes anyway. Maybe just give it a try it and see if it works. – Mike Jan 19 '21 at 01:45
17

jQuery version:

$(window).unload(function(){
    // Do Something
});

Update: jQuery 3:

$(window).on("unload", function(e) {
    // Do Something
});

Thanks Garrett

Paras
  • 685
  • 6
  • 16
11

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.

davejoem
  • 4,902
  • 4
  • 22
  • 31
10

You can use window.onbeforeunload.

window.onbeforeunload = confirmExit;
function confirmExit(){
    alert("confirm exit is being called");
    return false;
}
Gurpreet Singh
  • 20,907
  • 5
  • 44
  • 60
5

The event is called beforeunload, so you can assign a function to window.onbeforeunload.

Phssthpok
  • 1,629
  • 1
  • 14
  • 21
2

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.

Marcus
  • 261
  • 2
  • 5