Onload Collisions and How to Fix them
Harry Potter and the Onload Collisions
There is no other variable more fought over then the coveted window.onload. Its job is to run a function after a page has been loaded. The problem is that there is only one variable, and therefore only one function can run. Once you have set this you would think you are in the clear. That is until another script comes in and overwrites onload.
But all can be made well in JavaScript land once one of four choices is made. I give four ways to get around this problem, and suggest a way for modern browsers to be rid of this problem once and for all.
JavaScript, love it or hate it, it's here to stay. In the last generation of web design it was abused to accomplish silly things like turning your mouse into a analog clock or, my favorite, change the color of the background depending on where your mouse was on the page (I almost had a seizure when I first saw that one).
Since then we have learned to separate our pages into three parts: Information (HTML), Presentation (CSS), and Function (JS). The latter is where I have found my niche in recent years. The web is now flooded with JavaScripts that do everything. Most of which require the page to be loaded before doing any work. This isn't an issue because there is a variable aptly named window.onload. To make a function run after the page has loaded, you just assign it to this variable.
And there in lies the problem: there is only one variable. Only the last script that assigns to this variable will run successfully. In the past this generally wasn't an issue because you were the author and rarely had mismanaged conflicts. But times have changed.
With only one onload variable what are we to do. Well, I have discovered 4 options: check and wrap it, make onload an array, wrap onload with a function, make onload a function that sends an event. Each of these have their pros and cons. I'll discuss them briefly below.
Check and Wrap
This method is a quick check to see if anything is already set to run onload. If there is, then you create a function that first runs what ever was in the onload, then does what ever you need it to. It should look something like this:
if(window.onload != undefined) {
var tempFunct = window.onload;
window.onload = function() {
tempFunct();
yourFunction();
}
} else window.onload = yourFunction;
The advantage of this is that there is that you don't have to rewrite any old code, nor do you have to load any code before had. But the downsides are numerous. First, if you find yourself with a dozen onloads, then you will be in a very deep chain of functions, and that will slow things down. Also, if your tempFunct variable is overwritten then a link in the chain is broken, and nothing after it will run. Lastly, if someone changes the onload variable after you, your whole chain is gone.
Onload Array
As a way of getting around these problem, I believe that modern browsers should allow onload to be an array of functions. The browser could then iterate over the array, running every function it encounters. This would allow for many more functions to get access to this crucial resource. I came up with a quick way to accomplish this now in preparation for browser integration.
window.onload = function() {
for(var key in onload.functs)
onload.functs[key]();
}
onload.functs = [];
onload.push = function(funct) {
onload.functs.push(funct);
}
This would give the onload variable the ability to have the push method to add functions. To add a function, you would use:
onload.push(yourFunction);
// OR
onload.push(function() {
yourFunction();
});
Simple, isn't it? This fixes most of the cons in the check and wrap method, but still have some problems. If another script still uses the old onload method then, again, the whole chain is broken.
Onload Wrapper
The major problem with the Onload Array is legacy code messing up your functions. You could update your code, but that is a lot of work. So there is the thrid method, the onload wrapper. This is very similar to the onload array in the sense that it stores functions in an array and pulls them out using onload. But the difference is that you call a function to add it to the onload. This function can then detect if old code has overwritten the onload variable. The code is as follows:
var onDomReady = function(funct) {
if(window.onload != onDomReady.run) onDomReady.functs.push(window.onload);
onDomReady.functs.push(funct);
window.onload = onDomReady.run;
}
onDomReady.run = function() {
for(var key in onDomReady.functs)
if(onDomReady.functs[key]) onDomReady.functs[key]();
}
onDomReady.functs = [];
To use this method you:
function onLoadKiller() {
alert('onload killer');
}
onload = function() {
alert('onload');
}
onDomReady(function() {
alert('onDomReady');
});
onload = onLoadKiller;
onDomReady();
The code above will display three alert boxes. The first one is caused because the onload variable is set to a function that created the 'onload' alert. The onDomReady function then adds both the onload (the 'onload' alert) and the 'onDomReady' alert. Then the onload gets overwritten by onLoadKiller. We take that pecky guy out by running the onDomReady() function again, which fixes the chain. (And all is well in JS land)
This gets around all the set backs of the first two methods, except that, again, setting that pesky onload variable will blow out your chain. Luckily, this code has a way to get around that by simply rerunning the onDomReady() function without any parameters. This will allow you to insert this line of code where ever you find a colision and it will repair the chain. There is always a downside, namely that if two places set onload consecutively without a onDomReady() in-between, the first will be lost.
Event Driven Onload
The final method uses something completely different for the onload. Rather then setting this variable, you attach a function to an event (the 'load' event). This gives you the ability to add multiple functions to onload without collisions. Events are exactly how Prototype and other frameworks deal with onload. The downside is again that the change will require a rewrite of old code, and there for isn't much better then the first two.
Browser Improvements
As I hinted at earlier, I believe we need a way to natively allow more functions to run onload. The easiest way I see to accomplish this is to allow the onload variable to be an array, and run every function associated with it. While this wouldn't fix some of the problems in old scripts, it would be a way to move foreward and allow many more scripts to be run concurently within the browser.
Conclusion
So what's a scripter to do? There is no good answer for this. Each situation is different. Below I break it down into which category each method is useful for.
If you are starting your code base from scratch, I would suggest the Onload Array. This would give fall-foreward compatibility and would start to pressure browser makers to include this kind of onload scheme.
If you already have a code base, but know it fairly well, then the Onload Wrapper is a good option. It is the most forgiving when breaking the chain, and will repair itself as much as possible.
If you have a large code base, and really don't know how things interact, then the check and wrap is a good choice. It will protect your little chunk of code while preserving anyone else's (assuming you are tagging on to the end of the chain).
Lastly, if you are writing a script from scratch, and it needs to interact with an unknown environment, then your best bet is to go event driven. It's like taking a higher path where you don't monkey around with those lesser scripts still using the onload variable.
But all can be made well in JavaScript land once one of four choices is made. I give four ways to get around this problem, and suggest a way for modern browsers to be rid of this problem once and for all.
JavaScript, love it or hate it, it's here to stay. In the last generation of web design it was abused to accomplish silly things like turning your mouse into a analog clock or, my favorite, change the color of the background depending on where your mouse was on the page (I almost had a seizure when I first saw that one).
Since then we have learned to separate our pages into three parts: Information (HTML), Presentation (CSS), and Function (JS). The latter is where I have found my niche in recent years. The web is now flooded with JavaScripts that do everything. Most of which require the page to be loaded before doing any work. This isn't an issue because there is a variable aptly named window.onload. To make a function run after the page has loaded, you just assign it to this variable.
And there in lies the problem: there is only one variable. Only the last script that assigns to this variable will run successfully. In the past this generally wasn't an issue because you were the author and rarely had mismanaged conflicts. But times have changed.
With only one onload variable what are we to do. Well, I have discovered 4 options: check and wrap it, make onload an array, wrap onload with a function, make onload a function that sends an event. Each of these have their pros and cons. I'll discuss them briefly below.
Check and Wrap
This method is a quick check to see if anything is already set to run onload. If there is, then you create a function that first runs what ever was in the onload, then does what ever you need it to. It should look something like this:
if(window.onload != undefined) {
var tempFunct = window.onload;
window.onload = function() {
tempFunct();
yourFunction();
}
} else window.onload = yourFunction;
The advantage of this is that there is that you don't have to rewrite any old code, nor do you have to load any code before had. But the downsides are numerous. First, if you find yourself with a dozen onloads, then you will be in a very deep chain of functions, and that will slow things down. Also, if your tempFunct variable is overwritten then a link in the chain is broken, and nothing after it will run. Lastly, if someone changes the onload variable after you, your whole chain is gone.
Onload Array
As a way of getting around these problem, I believe that modern browsers should allow onload to be an array of functions. The browser could then iterate over the array, running every function it encounters. This would allow for many more functions to get access to this crucial resource. I came up with a quick way to accomplish this now in preparation for browser integration.
window.onload = function() {
for(var key in onload.functs)
onload.functs[key]();
}
onload.functs = [];
onload.push = function(funct) {
onload.functs.push(funct);
}
This would give the onload variable the ability to have the push method to add functions. To add a function, you would use:
onload.push(yourFunction);
// OR
onload.push(function() {
yourFunction();
});
Simple, isn't it? This fixes most of the cons in the check and wrap method, but still have some problems. If another script still uses the old onload method then, again, the whole chain is broken.
Onload Wrapper
The major problem with the Onload Array is legacy code messing up your functions. You could update your code, but that is a lot of work. So there is the thrid method, the onload wrapper. This is very similar to the onload array in the sense that it stores functions in an array and pulls them out using onload. But the difference is that you call a function to add it to the onload. This function can then detect if old code has overwritten the onload variable. The code is as follows:
var onDomReady = function(funct) {
if(window.onload != onDomReady.run) onDomReady.functs.push(window.onload);
onDomReady.functs.push(funct);
window.onload = onDomReady.run;
}
onDomReady.run = function() {
for(var key in onDomReady.functs)
if(onDomReady.functs[key]) onDomReady.functs[key]();
}
onDomReady.functs = [];
To use this method you:
function onLoadKiller() {
alert('onload killer');
}
onload = function() {
alert('onload');
}
onDomReady(function() {
alert('onDomReady');
});
onload = onLoadKiller;
onDomReady();
The code above will display three alert boxes. The first one is caused because the onload variable is set to a function that created the 'onload' alert. The onDomReady function then adds both the onload (the 'onload' alert) and the 'onDomReady' alert. Then the onload gets overwritten by onLoadKiller. We take that pecky guy out by running the onDomReady() function again, which fixes the chain. (And all is well in JS land)
This gets around all the set backs of the first two methods, except that, again, setting that pesky onload variable will blow out your chain. Luckily, this code has a way to get around that by simply rerunning the onDomReady() function without any parameters. This will allow you to insert this line of code where ever you find a colision and it will repair the chain. There is always a downside, namely that if two places set onload consecutively without a onDomReady() in-between, the first will be lost.
Event Driven Onload
The final method uses something completely different for the onload. Rather then setting this variable, you attach a function to an event (the 'load' event). This gives you the ability to add multiple functions to onload without collisions. Events are exactly how Prototype and other frameworks deal with onload. The downside is again that the change will require a rewrite of old code, and there for isn't much better then the first two.
Browser Improvements
As I hinted at earlier, I believe we need a way to natively allow more functions to run onload. The easiest way I see to accomplish this is to allow the onload variable to be an array, and run every function associated with it. While this wouldn't fix some of the problems in old scripts, it would be a way to move foreward and allow many more scripts to be run concurently within the browser.
Conclusion
So what's a scripter to do? There is no good answer for this. Each situation is different. Below I break it down into which category each method is useful for.
If you are starting your code base from scratch, I would suggest the Onload Array. This would give fall-foreward compatibility and would start to pressure browser makers to include this kind of onload scheme.
If you already have a code base, but know it fairly well, then the Onload Wrapper is a good option. It is the most forgiving when breaking the chain, and will repair itself as much as possible.
If you have a large code base, and really don't know how things interact, then the check and wrap is a good choice. It will protect your little chunk of code while preserving anyone else's (assuming you are tagging on to the end of the chain).
Lastly, if you are writing a script from scratch, and it needs to interact with an unknown environment, then your best bet is to go event driven. It's like taking a higher path where you don't monkey around with those lesser scripts still using the onload variable.



The proper way to do this:
attachEvent: http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx
addEventListener: http://developer.mozilla.org/en/docs/DOM:element.addEventListener
Any of the popular javascript libraries out there will abstract those two calls into one for you.
Left by Anon | Aug. 13, 2008 at 3:06pm
Leave a Comment