thomasfrank.se

When & why onload fails

June 9, 2006

It fires here, but does not there,
Some wish it would fire everywhere.
It could have been heaven -- if consistently sent:
That damn elusive onload event.


Opera, Safari and Firefox 1.5 (and newer) don't trigger the onload event when you hit the back and forward buttons. First thought to be a bug by some web developers, this is "by design" and because those browsers cache the page in its current state:

Just like a hidden tab

In many aspects you can think of this as if the page were still open in another hidden tab and gets shown again when you hit the back button.
This often works fine -- since results of JavaScript DOM manipulations stay put we don't have to invoke code that performs these manipulations again, which makes for faster page reloads. We can't really complain about that.
So we don't really need to fire a onload event that tries to do these manipulations once more! What we need is another event, that lets us perform som minor (or major) JavaScript tricks on reload of the page. (For example we might want to reset a certain dynamic menu.)

My solution

My solution to the problem is to take advantage of the fact that the browser pauses setTimeout calls when you leave the page, but continues them when the page is reloaded.
I've written a small object called checkReload:
  1. Use checkReload.init(function) to make the page run a certain function on reload.
  2. Place a checkReload.ignore() before any dialogs (alerts, prompts and confirms) in your code.

The latter is necessary since dialogs pauses our interrupts/setTimeouts as well -- the ignore method lets us avoid any false firering of our reload function.
The heart of checkReload is a check performed once a second, roughly -- all browsers don't time interrupts too well. If the time between cycles is more than 1200 milliseconds we will assume that the page has been reloaded and therefore trigger the function set with the init method.
If you got a lot of interrupts and/or event driven things happening on your page you may have to increase "tolerance" and use a higher value than 1200. This will only get you into trouble if the user is really trigger happy with the back and forward buttons.
checkReload={
	checker: function(x){
		if (x && !this.ignorer && new Date().getTime()-x>1200){this.reloader()};
		this.ignorer=false;
		setTimeout("checkReload.checker("+new Date().getTime()+")",1000)
	},
	init: function(x){
		this.reloader=x;
		this.checker()
	},
	ignore: function(){
		this.ignorer=true	
	}
};

function loader(){
	checkReload.ignore();
	alert("Onload fires!")		
};
onload=loader;

function reloader(){
	alert('Reload fires!')
};
checkReload.init(reloader);

You can download this example or just the checkReload object. You can also try it out on an example page. (If you want to try out the code on your own pages, bear in mind that onload will always fire when you load a page locally, directly from your hard disk -- so use a web server.)

New events in Firefox 1.5

The Firefox developers have given the whole thing some thought and therefore introduces two new event handlers - pageshow and pagehide (corresponding to onload and onunload).
I have tested my script in Firefox 1.5, Opera 8 and Safari 2.0 - seems to work fine. Still you might want to try out the new Firefox event handlers as well.

Oldtime onload in IE 7.0

Does Microsoft follow the path of Firefox, Opera and Safari in IE 7.0? No, why should they. Why break tradition? As far as I can tell onload fires every time on back/forward in IE 7.0 Beta 2.
If you asked me a month ago I would have said that this is good. Now I'm not absolutely sure anymore. But the important things is to be able to understand the design choice behind a new kind of browser caching and how to handle it.
[comments]