Unload event never fires in IE

April 8th, 2008 by Spocke

We recently found a serious bug in IE where the unload event wouldn’t fire on a specific page we had on a site. After some bug tracking we found out that the unload event never fired since all the contents of the page hadn’t finished loading before we navigated to another page.

This is a major problem since the unload event is commonly used to clear circular references etc in IE to prevent memory leaks. So this bug makes all Ajax libraries/frameworks out there that depend on the unload event on IE to fail if the page is unloaded before the contents of the page finished loading.

Here is an example of the bug, run the page in IE and follow the instructions on the page. Below is JS source code that is used on that page.

function unload() {
	alert('Unload event occured.');
};

window.attachEvent('onunload', unload);

After some digging around we finally found a solution for the problem. The beforeunload event is fired correctly in IE but since this event can be blocked by setting the returnValue to an string value we couldn’t just add the memory cleanup logic there since the user might press cancel on the beforeunload event confirm dialog and then we would have removed all events on the page. So we needed to detect when the page was unloaded and we found out that the stop event can be used to detect this. It fires when the page is unloaded but unfortunately it also fires when the user pressed the stop button in the browser so we needed to add a fix for that as well.

Here is an example of the fixed version. You notice that this will fire the unload event correctly. Below is JS source code that is used on that page.

function fixUnload() {
	// Is there things still loading, then fake the unload event
	if (document.readyState == 'interactive') {
		function stop() {
			// Prevent memory leak
			document.detachEvent('onstop', stop);

			// Call unload handler
			unload();
		};

		// Fire unload when the currently loading page is stopped
		document.attachEvent('onstop', stop);

		// Remove onstop listener after a while to prevent the unload function
		// to execute if the user presses cancel in an onbeforeunload
		// confirm dialog and then presses the stop button in the browser
		window.setTimeout(function() {
			document.detachEvent('onstop', stop);
		}, 0);
	}
};

function unload() {
	alert('Unload event occured.');
};

window.attachEvent('onunload', unload);
window.attachEvent('onbeforeunload', fixUnload);

The only problem we still have is that if you hit F5 and force a refresh the unload event will still not fire. So if anyone has some good ideas on how to fix that it would be more than welcome.

Posted in Development

13 Responses

  1. Bramus! Says:

    Some clever bughunting! Are all IE versions affected (IE6 & IE7 & IE8b1), or is it a specific one?

  2. Martin Says:

    Okay, it’s really a hack, but it’s internet explorer…

    Why do you don’t use a keycode to listen for the F5? Okay, it wouldn’t triggered if you hit the refresh button.

    Greetings,
    Martin

  3. Spocke Says:

    @Bramus: It affects IE6 and IE7 haven’t tested IE8.
    @Martin: Yes, it’s a real ugly hack. Yes, trapping F5 might work just fine I will fiddle somewhat with that.

  4. Bram.us » TinyMCE 3.0.7 Released Says:

    [...] table or add classes to it. This was also added to normalize the browser behavior. We also fixed a new type of memory leak in IE” – Announcement – Download – Changelog Spread the [...]

  5. Ajaxian » Are you sure your unload handler is firing in IE? Says:

    [...] Sörlin found that sometimes his unload event never fired in IE: We recently found a serious bug in IE where the unload event wouldn’t fire on a specific page [...]

  6. Nicolas Says:

    I hardly see this one as a bug: since the onload event didn’t trigger in your example, I can understand why the unload do not as well.
    So, if you use the onload to attach and onunload to detach your events, you’re not leaking.
    But if you use a “DOMReady” technique, you’re in trouble if you use the onunload. You should use some kind of “DOMDisposed” technique then.

  7. Spocke Says:

    This is a bug since the unload event should always fire if a document is unloaded. I don’t see how this has anything to do with DOMReady you tend to want to add events before all contents is loaded on the page DOMReady is normally fired before the onload event and that is the problem.

    You must remove the event handles on unload in IE if you use closures or it will leak and the only way to do that is to use the unload or beforeunload events.

  8. Javascript News » Blog Archive » Are you sure your unload handler is firing in IE? Says:

    [...] Sörlin found that sometimes his unload event never fired in IE: We recently found a serious bug in IE where the unload event wouldn’t fire on a specific page [...]

  9. Mathew Robertson Says:

    A question for those smarter than me… :)

    Wouldn’t the “onbeforeunload” cause a leak (aka the JS/DOM style leak), since it is never detached?

    ie: shouldn’t the setTimout() also detach the “fixUnload”?

  10. Spocke Says:

    The fixUnload doesn’t leak in the example since it’s not used inside a closure. But the stop function is so it needs to be properly detached.

    This code is more of a proof of concept than a real usable code piece normally you would contain it in a library and then you need to fix it up a bit.

  11. sawan Says:

    this code works good for problem mentioned above , but my problem is to stop unloading of document on the event onbeforeunload.

  12. slabit Says:

    I have been after the Internet for this info and just wanted to say thanks to you for this post. BTW, just off topic, where can i get a version of this theme? – 10x

  13. Spocke Says:

    The theme is made by Arcin. Check the footer and the bottom right corner.