12

Is there such a thing?

I know that I can hook my function on the click event of all links, but there are other situations where a page is changed, like refresh or when a different script changes the window.location


In the end, I did it by sending a string trough postMessage from the unload event, like this:

$(window).bind('unload', function(e){
  window.parent.postMessage('unloading');
});

in the parent document:

$(window).bind('message', function(e){     
  if(e.originalEvent.data == 'unloading'){
    // ajax stuff here
  }
});

It appears to work. I probably should have mentioned that there's a iframe involved :)

Alex
  • 66,732
  • 177
  • 439
  • 641
  • but doesn't that run after page is loaded? I need it before so I can fire a ajax request and update some things, then load the page – Alex Jun 24 '12 at 11:57
  • You can't really rely on the ability to do something before a page is unloaded. The user might, for example, shut the computer down abruptly. – Pointy Jun 24 '12 at 12:03

3 Answers3

29

There's the beforeunload event, which is fired when the page is being torn down (either to follow a link, or if the window is being closed, or refresh, etc.). Example:

window.onbeforeunload = function(event) {
    var s = "You have unsaved changes. Really leave?";

    event = event || window.event;
    if (event) {
        // This is for IE
        event.returnValue = s;
    }

    // This is for all other browsers
    return s;
}

There are, for obvious reasons, very strict limits on what you can do in the handler of the beforeunload event, and as you can see above beforeunload handlers have a different signature than normal event handlers. Basically, your code can't do anything asynchronous, can't open new windows, and can't cancel the event. It can return a string, and if it does, the browser will pop up a window asking whether you really want to leave the page, and including your string in that pop-up.

From your comment on the question:

I need it before so I can fire a ajax request and update some things...

The way to do that here many years after the question was originally asked is with the beacon API. This lets you send a non-blocking asynchronous request to the server without slowing down the process of the browser tearing down your page and navigating to the next:

navigator.sendBeacon("/path/to/notify", optionalData);

It's a send-and-forget, but the browser doesn't cancel it when your page is torn down (like it does a standard asynchronous ajax request). Instead, it allows that request to complete even though your page has been removed.

Back in 2012 when this answer was originally written, you could usually get away with a synchronous ajax call (async: false) provided it didn't take too long. But you can't reliably do that now (and it was never a good idea, it holds up the UI).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Using synchronous ajax in the event has worked for me (There goes **never** use sync ajax :D) – Esailija Jun 24 '12 at 12:01
  • @Esailija: Yes, it's one of the few use cases for synchronous ajax (although introducing a delay like that isn't great UX). As I said, you can't do anything *asynchronous*. – T.J. Crowder Jun 24 '12 at 12:02
  • I'm not really sure if the browser actually freezes if the request takes too long, it will just not wait for response and close the page? – Esailija Jun 24 '12 at 12:03
  • 1
    @Esailija but once ajax request is sent then it will do it work even after the page is closed –  Jun 24 '12 at 12:05
  • I just tested in chrome and if you refresh, it will wait at least 5 seconds ( I had `sleep(5);` on server-side). If you close the tab, it will wait for 1 second (my approximation) and then close it. – Esailija Jun 24 '12 at 12:06
  • @Alex: In this context, a *synchronous* request makes the code wait (locking up the browser until the request complets), an *asynchronous* call lets JavaScript on the page keep running -- later when the request completes, your `success` or `error` handler is called. – T.J. Crowder Jun 24 '12 at 12:07
  • @Esailija: On the tab, was that how long it took the tab to disappear from the UI? Because remember Chrome keeps tabs around (for the "reopen tab" feature). – T.J. Crowder Jun 24 '12 at 12:08
  • @Somebodyisintrouble not if the server didn't take your data early enough T.J. Crowder, Not sure what you mean. Normally when I close a tab, it instantly disappears. With synchronous ajax, it waited at least 1 second before the tab disappeared. – Esailija Jun 24 '12 at 12:08
  • @Esailija: Thanks. What I mean is that although Chrome removes the tab from the UI, the window and structures for it may not be removed from memory, so that the "Reopen tab" feature works quickly. (Or, of course, it may well.) So it could be that the request isn't killed after a second, even though the tab disappears from the UI. or it could be that it is. (Thanks for the info on this, btw.) – T.J. Crowder Jun 24 '12 at 12:15
  • @T.J.Crowder what if the window is closed directly –  Jun 24 '12 at 12:16
  • @Somebodyisintrouble I tested a chrome window where the sole tab was the test tab and it took 1 second to close the chrome window as well. (Normally closes instantly) – Esailija Jun 24 '12 at 12:17
  • @Somebodyisintrouble: Just tried it: I opened a window and navigated to a site, then closed the window. In my remaining window I did "Reopen tab" and it re-opened the window and put the site back! Note this is different from Esailija's also-useful test. – T.J. Crowder Jun 24 '12 at 12:18
  • You're supposed to have worker thread that can do async ajax queries in the background if the user closes the tab "too soon". Of course, that's much harder to implement than just using syncronous ajax but the syncronous ajax query will stall the UI until it completes. – Mikko Rantalainen Jun 13 '21 at 16:08
  • @MikkoRantalainen - Here in 2021, you're supposed to use the [beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API). :-) – T.J. Crowder Jun 13 '21 at 16:33
6

jQuery has unload function:

The unload event is sent to the window element when the user navigates away from the page. This could mean one of many things. The user could have clicked on a link to leave the page, or typed in a new URL in the address bar. The forward and back buttons will trigger the event. Closing the browser window will cause the event to be triggered. Even a page reload will first create an unload event.

Note that this should be binded to window object instead of document:

$(window).unload(function() {
    // do something
});

You can also bind handler to beforeunload event:

$(window).bind('beforeunload', function() {
    // do something, preferably ajax request etc
    return 'are you sure?';
});
Zbigniew
  • 27,184
  • 6
  • 59
  • 66
  • The comments on that jQuery documentation page suggest that this event might not work too reliably. – Pointy Jun 24 '12 at 12:00
  • hm.. in Chrome I get an error in the console: Blocked alert('unloading...') during unload. – Alex Jun 24 '12 at 12:01
  • You don't need to do `confirm` or `alert` you simply return a string and that will be displayed in the confirmation box. With `confirm` etc. you get `Blocked confirm('are you sure?') during beforeunload.` – Esailija Jun 24 '12 at 12:21
  • @Esailija Thanks a lot. I,ve missed this part. Now it works like it should :) – Zbigniew Jun 24 '12 at 12:33
1

When a page is reloaded, whatever was there before will be gone. Thus, it seems like what you're talking about is something you'd do at DOMReady or "load" in the new page, since you can't "push" code from the former page into the new context.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • @Somebodyisintrouble the hash value isn't really part of the page; it's part of the URL, and it only transfers if the new URL uses the same hash. If you're on `http://foo.bar.com/xyz#hello` and you click a link to `http://cnn.com/international`, the old hash value won't be available in the new page. – Pointy Jun 24 '12 at 12:02
  • @Somebodyisintrouble there's no mention of that one way or the other in the question. – Pointy Jun 24 '12 at 12:03
  • no. If the link goes to another domain then I don't care if my ajax request does not fire. I only need to update some (temporary) stuff, that are used on the next page from the same site.. – Alex Jun 24 '12 at 12:05