This week I learned about DOM events. In my work at ServiceNow, we’ve been working hard on keeping our strong product functionality while modernizing the user experience.

Dirty form warnings using onbeforeunload

Dirty form warnings are those messages that you see when you’re trying to leave a page with unsaved work. While they have come under fire recently in some circles link, they can be very helpful if used properly.

window.onbeforeunload = function() {
	if (isFormModified())
		return "You have unsaved work. Are you sure you want to leave this page?";
}

If you assign a function to onbeforeunload, you can return a message that it will display. This code may result in the following message:

Dirty form warning

Now when do these events fire, anyway? Per the W3C spec, they fire like this:

  1. User makes an action to leave the page, which may be:
    • clicking a link
    • submitting a form
    • closing the tab
    • closing the browser
  2. The onbeforeunload event is fired and the handlers are run
  3. Should a string be returned from the unbeforeunload event, that message shows in the unload popup
  4. If the user motions to stay on the page, the entire process is stopped here
  5. If the user motions to leave the page, then the action desired is actually performed. Note that for clicking a link or submitting a form, this will cause the request to be sent for the new page
  6. When the browser starts receiving new data to display, the unload event is fired and those handlers are run

There are some retrictions when writing handlers for beforeunload and unload events. You likely cannot run AJAX because you have no guarantee that the response will come back before the page is unloaded1. Likewise, browsers block calls to methods such as window.alert() and window.confirm() See W3C.

When the user stays on the page

It turns out once a user has triggered an onbeforeunload event, there are two ways in which he/she will stay on the page:

  1. He/she motion to stay on the page, such as by clicking “Stay on this Page”
  2. The destination URL was to a file download, which actually did not leave the page

One difference during these use cases is that while the beforeunload event fires, the unload event does not. If you can, one way to deal with these cases is to register unload events rather than beforeunload.

If you only need to detect the user staying on the page, you can find it by setting a timeout into the near future. The timeout will not execute while the popup is present, as the page is paused. Additionally, the timeout will not execute if the user chose to leave the page:

window.onbeforeunload = function() {

	setTimeout(function() {
		alert("You survived the popup and can continue to enjoy this page")
	}, 750);

	if (isFormModified())
		return "You have unsaved work. Are you sure you want to leave this page?";
}

If you need to detect file downloads, things can get trickier. This is because this process may seem identical to submitting a real form. I’m not going to go into depth here about what code to use, but here is a solution that has worked for me:

  1. For the legitimate request, gate the requests one by one (in Java, we use a synchronize block)
  2. In your setTimeout, send an AJAX request that will be gated as well

Since your requests are gated, you will only get the AJAX response if you are still on the page. That worked for me.

1I have found that you can use synchronous AJAX here, but that doesn’t mean it’s a good idea

Extra reading:
MDN: window.onbeforeunload
StackOverflow: Can beforeunload be used to send XMLHttpRequests reliably
W3C HTML5 - Unloading Documents