32

I need to call a JavaScript/jQuery function which has a few lines of code in it, on a PHP page when the user closes his window/tab or navigates away by clicking a link. I've tried the onbeforeunload function but only the return "blah blah blah;" part executes and everything else is ignored. I've also tried the .unload method from jQuery but for some reason this code doesn't run.

$(window).unload(function() {
    alert('blah blah blah');
});

Please suggest alternatives. Thanks..

CobaltBabyBear
  • 2,188
  • 6
  • 26
  • 47
  • 1
    What do you want to do on unload? – writeToBhuwan Apr 10 '13 at 12:02
  • 2
    I need to do an Ajax call to PHP to unset a session variable. – CobaltBabyBear Apr 10 '13 at 12:03
  • `navigates away by clicking a link` this shows you have to bind unload on your document – muneebShabbir Apr 10 '13 at 12:04
  • 1
    `$(window).on('beforeunload', function() { ... });`, but you can't stop the page from unloading, so you only have a short amount of time to do stuff, and the question is if what you're trying to do takes too long, and the page unloads. – adeneo Apr 10 '13 at 12:04
  • possible duplicate of [How to use JS/jquery to confirm when navigating away from page](http://stackoverflow.com/questions/7296677/how-to-use-js-jquery-to-confirm-when-navigating-away-from-page) – Nerdroid Apr 13 '15 at 05:49

4 Answers4

25

Here is a simple working example. Whatever you return from the unload callback will be displayed in a browser popup confirmation.

Working example sending Ajax request before unload http://jsfiddle.net/alexflav23/hujQs/7/

The easiest way to do this:

window.onbeforeunload = function(event) {
    // do stuff here
    return "you have unsaved changes. Are you sure you want to navigate away?";
};

in jQuery:

$(window).on("beforeunload", function() {
    $.ajax("someURL", {
        async: false,
        data: "test",
        success: function(event) {
             console.log("Ajax request executed");
        }
    });
    return "This is a jQuery version";
});

Look into the Network tab of the browser. You will see how the request is being sent as you wanted to do. Just send the appropriate data.

Bear in mind all operations triggered must be synchronous, so you can only make synchronous ajax requests for instance. However, the above is not entirely reliable for any purpose.

Opt for periodic back-up of user data to localStorage and sync with the server automatically . Keep window.onbeforeunload just as an extra precaution, but not as a main mechanism. It's well known to cause problems.

flavian
  • 28,161
  • 11
  • 65
  • 105
  • This does not work. The beforeunload is always called, whether the user confirms the warning or not. I need to run some cleanup but i only want to run the clean up if the user ACTUALLY navigates away. How can I do that? – John Henckel Apr 26 '16 at 20:33
  • @JohnHenckel There is no way to do that. – apscience May 24 '16 at 00:32
  • 1
    @apscience, thanks. Seems like this is a flaw in the browser spec. For example, I want to make a web page to edit a row in a database, and I want to LOCK the row to prevent other users from changing it. The only solution I found is to send a heartbeat to the server every few secs, so that the server knows when to UNLOCK. However, this has a serious flaw, because while any "confirm" box is open, the JS engine is paused, and therefore the heartbeat stops, even though the user is still editing! Maybe someday the browser will replace the OS, but for now it sucks as an application platform! – John Henckel May 24 '16 at 14:33
  • 3
    Sorry late to the party but to accomplish this you can use a combination of [onbeforeunload](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload) and [onunload](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onunload). You display the warning text in the return of the onbeforeunload and any clean up in the onunload. onunload will not be called if you select the stay option. Be sure to use synchronous calls in your onbeforunload and onunload. Can provide some examples if you need some help. – Tyler Mar 10 '17 at 00:23
  • @Tyler 4 years fly by :) – flavian Mar 10 '17 at 04:08
  • @JohnHenckel there are other failure modes besides navigating away from the page, e.g. what if the user's machine crashes? what if they are on a laptop on the train and go through a tunnel and lose connectivity? What if the user steps away from the computer while the page is open and never comes back. These are all ways the row can remain locked indefinitely. Consider a solution that doesnt rely on long-lived database locks for concurrency control, e.g. optimistic concurrency control, record versioning. For what you are describing i think a heartbeat is actually the best solution. – Tom Lubitz Feb 09 '18 at 17:34
  • @Tyler Can you please post an answer showing your solution using `onbeforeunload` and `onunload`? – Travis Heeter Jan 09 '19 at 15:50
  • @TravisHeeter not enough room in comments to add the answer directly. So here is a link https://gist.github.com/snaveevans/141e8576f45539efdeb0b50f2c31cedd Please note that browsers won't show the confirmation dialog if the user hasn't done anything on the page. – Tyler Jan 09 '19 at 17:37
  • @Tyler - why not add an answer? Like in the "Your Answer" Section? You get fake internet points... – Travis Heeter Jan 09 '19 at 17:40
  • return "message" may not work. In some browsers, you need to user event.returnValue = "message" instead. https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload – foxontherock Jan 21 '19 at 21:14
  • 1
    @foxontherock 2013 answer, this is probably not longer up to date. – flavian Jan 21 '19 at 23:05
  • Yeah, this does not seem to be a happy answer in 2021 on Version 92.0.4515.131. I have, however, been able to write to cookies before the event executes. – ruffin Aug 16 '21 at 21:07
2

This is an old question, but I wanted to share an alternative approach that has the benefit of working with high consistency:

Establish a WebSocket connection to the server, and when the client navigates away the WebSocket connection will be closed. Server-side, you can detect the closed connection in a callback and run whatever code you need on the server.

Executing Javascript on page unload is often unreliable (as discussed in the other answer) because it's inherently at odds with the user's intention. This method will always work, although it is admittedly quite a bit more cumbersome to implement.

This does change the context of your "run before leaving" code from client-side to server-side, but I imagine for most cases the difference is inconsequential. Anything you want to run client-side before the client leaves your page is probably not going to change anything the client sees, so it's probably fine to run it server side. If there is specific data you need from the client you can send it through the WebSocket to the server.

The only situation I can think of off the top of my head where this might cause unexpected behavior is if the user loses the WS connection without actually navigating away, e.g. they lose internet or put their computer to sleep. Whether or not that's a big deal is probably highly dependent on what kind of code you're trying to execute.

rococo
  • 2,280
  • 2
  • 22
  • 37
0

In many projects of mine, the mentioned methods here are instable. The only thing that works for me is to bind the event as original attribute on the body element.

<body onunload="my_function_unload()">

jQuery method:

$('body').attr('onunload', 'my_function_unload()');

From an iframe:

<body onunload="window.parent.my_function_unload()">

jQuery method:

$('<iframe />').load(function(){
    $body = $(this).contents().find('body');
    $body.attr('onunload', 'window.parent.my_function_unload()');
}

Also, important, no arguments in the attribute, and the function must be in the global window scope, otherwise nothing happens.

For example, common mistake If your my_function_unload() are wrapped inside a ;( function( $ ) {... OR $(document).ready(function(){... AS my_function_unload() must be outside that private scope. And dont forget to use jQuery instead of $ prefix then. (Working with Wordpress for example)

Jonas Lundman
  • 1,390
  • 16
  • 18
0

This is kind of a pain, as Chrome, at least in Version 92.0.4515.131, seems to be clamping the security screws on what you can get away with in beforeunload. I'm unable to make a synchronous ajax request, for example.

If there's any chance the user will be back to your site and you can wait until then to deal with their having closed a window (which does work in my use case), know that setting cookies does currently seem to be fair game during the beforeunload event. Even works when I close the browser. Covers most anything but power cycling the computer, it appears.

Here's a reference example (with getCookie stolen from this SO question):

function setCookie(name, value) {
    document.cookie =
        '{0}={1};expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/;SameSite=Lax'
            .replace("{0}", name)
            .replace("{1}", value);
}

// https://stackoverflow.com/a/25490531/1028230
function getCookie(cookieName) {
    var b = document.cookie.match('(^|;)\\s*' + cookieName + '\\s*=\\s*([^;]+)');
    return b ? b.pop() : '';
}

window.addEventListener('beforeunload', function (e) {
    console.log('cookie value before reset: ' + getCookie('whenItHappened'));

    var now = +new Date();
    console.log("value to be set: " + now);
    setCookie('whenItHappened', now);

    return "some string if you want the 'are you sure you want to leave' dialog to appear";
});
ruffin
  • 16,507
  • 9
  • 88
  • 138