21

recently, I had the urgent requirement to give my server a notice, that a specific page of my webapp is about to get closed. "Easy peasy" I thought, beforeunload is available for quite a while. The HTML5 "thing" even refreshed the spec (that was what I thought...) about it, in the way that we had the option to return a string value from a beforeunload event handler and stuff, which gives an user the option to intercept.

See the MDN page about onbeforeunload

However, as it turned out, there isn't any "official" specification available, which describes the behavior for beforeunload up to this date. The only official document I found, was on WHATWG, which is just a proposal for W3C of course.

See WHATWG

So far so good. We are able to create a synchronized XHR request within a beforeunload event handler. "Most" browsers, give that request a timeframe of about 1-2 seconds to complete, after that it is getting killed. Standard asynchronous request are killed immediately. Having that said, I cannot even tell from "where" I know this, it seems like gossip and word of mouth looking at it now. Even tho, it works in Firefox+Chrome, we cannot rely on that, can we ?

Is there any ongoing discussion/proposal on WHATWG about beforeunload ?
Any other official resources about the event I might have not found ?

And far most important to me here, how reliably can we send data via sync-XHR there ?

lebol
  • 433
  • 5
  • 12
jAndy
  • 231,737
  • 57
  • 305
  • 359
  • I'm using this event through YUI3 event handling (not that this matters for this question) and XHR to do a "fire and forget" async.(!) XHR call to the server (statistics logging for a big internal accounting app, to see which pages of the app users actually use for how long - I can log the duration of stay on the page only when they leave the page, after all). I tested it on FF (latest) and IE 7 and 8 - works. – Mörre Mar 18 '13 at 14:46

3 Answers3

20

Take a look at navigator.sendBeacon(), which allows you to reliably send data to a server even when the page is unloading. It's currently in a draft specification and supported by Firefox 31, Chrome 39 (behind a flag from 37), behind a flag in Opera 24.

You could "sort of" polyfill it using something like the following:

navigator.sendBeacon = navigator.sendBeacon || function (url, data) {
    var xhr = new XMLHttpRequest();

    // Need to send synchronously to have the best chance of data getting
    // through to the server
    xhr.open('POST', url, false);
    xhr.send(data);
};

Further reading:

Sam Potts
  • 86
  • 5
Andy E
  • 338,112
  • 86
  • 474
  • 445
  • nice answer. As I stated, I don't really even know where I have it from, all I know is that it **works**. After a specific timeframe, browser will cancel the request. Kind of the same deal they ignore `modal window calls`. I didn't know that Hixie said that, but it really sounds horrible. Of course such an event, even if well specced and implemented, can't cover all "*meteor hits earth*"-crash-cases, but it would probably be good enough for most *auto-save* or *notice* cases. That is for, tab/window close, redirect, browser-close. – jAndy Mar 18 '13 at 14:55
  • @jAndy: I think that's why it ended up in there unchanged. They didn't like the way it currently works, but it's a way of making compliant browsers include it because all the others already do for historical reasons. Like you said, there aren't many active discussions surrounding it, which is probably why it just didn't get any love from the specification editors when they added it. – Andy E Mar 18 '13 at 15:03
  • 2
    "*There is nothing worse, than an unspecified feature*" - (jAndy, 2013) – jAndy Mar 18 '13 at 15:06
  • 1
    I wonder how that such feature can be available since `XMLHttpRequest` is not the only async method? Why don't they introduce an event that can reliably handle asynchronous operations on page unload? Why??? – Lewis Jun 24 '15 at 07:04
  • 1
    Note: from Chrome 59 `navigator.sendBeacon` with json (or any non-simple CORS) content type will raise an exception. Apparently temporary until a security issue is addressed. See: See http://crbug.com/490015 – steevee Jun 20 '17 at 13:04
  • 1
    sync ajax no longer supported since chrome 73 related chrome feature: https://www.chromestatus.com/feature/4664843055398912 – cor3000 Sep 09 '19 at 02:47
  • I see it says now in the MDN documentation that this API is "intended to be used in combination with the visibilitychange event (but not with the unload and beforeunload events)" - so i don't think this is the answer. I have the same problem as the questioner, a sync xhr request issued on unload.... – Woody Mar 05 '21 at 09:50
4

The thing to keep in mind is that beforeunload started as an extension by Internet Explorer. Automatically, that makes it a second-class citizen on the web. There is no specification, and browser implementation varies. For example, Firefox only partially implements it by not displaying the string, only a generic message.

Additionally, even when fully implemented, it does not protect against all possible unload scenarios, eg, the user has terminated the processor, the browser has crashed, or the computer has been turned off. Even ignoring these extreme scenarios, I suspect that it might be possible to configure your browser to ignore such requests.

My feeling is that you shouldn't rely on this message to save you. If this web app is internal, I would suggest training them to use the Save or Close or whatever buttons instead of just closing the tab. If it's external, maybe look into automatic saving as the user does their thing?

Mike Caron
  • 14,351
  • 4
  • 49
  • 77
2

Sync XHR is a top-source of browser hangs, accounting for nearly 10% of hangs: http://blogs.msdn.com/b/ieinternals/archive/2011/08/03/do-not-use-xmlhttprequest-in-synchronous-mode-unless-you-like-to-hang.aspx

In IE, even sync XHR can be "interrupted" if the request requires Windows authentication roundtrips, or if there's a POST body to be sent. You may find that only the headers of the first unauthenticated request are sent.

EricLaw
  • 56,563
  • 7
  • 151
  • 196
  • I think you're missing the point there. As I mentioned, *sync xhr requrest* are the only way for sending data to a server within a *beforeunload* handler. Browsers will allow those kind of request, but they will abort it after a short period of time (which is a good thing). The interesting question actually is, why does it work that way and who defined this behavior + is it reliable. – jAndy Mar 18 '13 at 17:38