Jason

Experiments in Javascript: Multicast Delegate

Mar
03,
2011
by Jason
   LinkedIn  
There are quite a few blog posts out there about "doing window.onload the right way", albeit from 2007. I looked over them, and took issue with the hard-coded feel that they had.  For instance, a function that returns a function, but takes two functions, and no more, as arguments. I came across an instance where this just wasn't acceptable, according the new dogma of Javascript programmers out there, which is to write as few lines as possible. I explored new ways of accomplishing this. Read on to find out what I came up with.

First and foremost, don't use window.onload :) attachEvent and addEventListener are there for this exact purpose. Let's take a trip back 5 years and pretend this was relevant for window.onload. However it is still relevant for Javascript in general.

The problem of just outright setting window.onload to your function, is that it will overwrite whatever window.onload was set to previously. This can lead to malfunctioning pages that are very difficult to debug. That was the theory of all of the posts about "doing window onload the right way".

function foo(){ alert("foo"); }

window.onload = foo;

// somewhere else on the page, maybe 1000 lines below

function bar() { alert("bar"); }

window.onload = bar;

You can deduce that "foo" will not be alerted. This would be bad if our foo actually did something, like start a video of a cat doing silly things. Devastating. How can they both work?!

The first accepted method is to build a function inline that combines two functions:

window.onload = function(){ foo(); bar(); }

This will work if that's the only time window.onload is set. You can't call window.onload explicitly inside of window.onload unless you like infinite loops. However, storing the referenced function and updating window.onload to the new function is fair. With that knowledge, let's continue our investigation.

The current method out there to get the previous window.onload and the additional function to call correctly looks like this:

function doublecast(fn1, fn2){
return function(){
if (typeof(fn1) == "function")
fn1();
if (typeof(fn2) == "function")
fn2();
}
}

You would use this method to add to the window's onload functionality like so:

window.onload = doublecast(window.onload, foo);

If you needed to add more functions, you would do so in successive calls to "doublecast":

window.onload = doublecast(window.onload, foo);
window.onload = doublecast(window.onload, bar);

or through the ugly method of

window.onload = doublecast(window.onload, function(){ foo(); bar(); });

jQuery has made this acceptable, but it's still ugly! And the successive calls are not line-number friendly. Obviously, after reading John Resig's "Learning Advanced JavaScript", I feel I am ready to build a more appropriate and clean function.

function multicast(){
if (arguments == null || arguments.length == 0) return function(){ };

var fns = [], j = 0;
for (var i = 0; i < arguments.length; i++){
if (typeof(arguments[i]) == "function")
fns[j++] = arguments[i];
}

return function(){
for (var i = 0; i < fns.length; i++)
fns[i]();
};
}

Our new code looks like this:

window.onload = multicast(window.onload, foo, bar);

window.onload could have been set previously, and we don't care. We know we've defined two functions in the current module (say, an ASP.NET control or some other type of view) and it will work, so long as no one after us sets window.onload = myAwesomeLoadFunction; which would overwrite it... I guess no solution is completely future proof.

Discuss in the comments, please.

DELPHIC DIGITAL PHOTOS

Powered by Flickr

TWEETS

Powered by Twitter