On Event Handlers

The JavaScript Browser-Object Model (i.e. the bits of JavaScript that aren’t core language, but glue the language to the web browser) bears many legacies of the over-rapid development of features, bells and whistles from the late 90’s. One such blemish is the presence of two event models for hooking up user input (mouse movement and clicks, and keyboard input) to the code that runs it. The classic model is one event handler per input type per DOM element (e.g. onclick, onmouseover, etc.). The newer type is a sorry tale of the Good Luck - Bad Luck variety (if you follow the link, you’ll need to scroll down a little to find the story).

Good Luck! The newer event model is based on the Observer pattern (a.k.a. publish and subscribe), conferring greater flexibility and scalability than it’s predecessor.

Bad Luck! The implementation differs between the main two browsers (hands up who’s surprised by this news. No, I thought not). Mozilla offers addEventListener() and removeEventListener(), IE offers attachEvent() and detachEvent().

Good Luck! It’s possible to write a wrapper function around these implementations. Scott Andrew LePera created one on his blog, that has since been passed round the entire internet several times (well, that small, inbred subset of the internet that tinkers with javascript, anyway).

Bad Luck!Scott’s function doesn’t really fix everything - Peter-Paul Koch of Quirksmode fame gives the lowdown here. (In a nutshell, you can pass in a Function object to either implementation, but IE will fail to realise that the keyword this is a reference to the DOM node being clicked, moused over, or whatever.

Good Luck! You can get around the this problem by using closures. This solution was first suggested to me by on the WDF-Dom mailing list by Lon Boonen:

function addEvent(obj, evType, fn){
if (obj.addEventListener) {
obj.addEventListener(evType, fn, true);
return true;
} else if (obj.attachEvent) {
var f = function(evt) {
obj.__hnd = fn;
var result = obj.__hnd(evt);
obj.__hnd = null;
return result;
};
var r = obj.attachEvent("on"+evType, f);
return r;
} else {
return false;
}
}

(see the inline function being created, to trap the reference to the DOM< node obj?). Another approach that also uses closures under the hood is described by Sjoerd Visscher here.

Even Better Luck! Quirksmode have gone so far as to launch an addEvent() recoding contest, with Scott on the panel of judges.

Bad Luck! The contest closed on 22 September, so it’s too late to enter now, dear Reader.

OK, enough good and bad luck. I had a point to make when I started writing this up. In Ajax in Action Chapter 4, I give a run through the event handling models, and then advise readers to steer clear of the newer model. I then go on to show how to implement the Observer pattern on top of the classic model in Javascript. There are several neat ways around the shortcomings in the newer event model, but I’m inclined to stick by my original advice, for two reasons:

  • The eventRouter object that I describe in the book provides a generic implementation of Observer/pub-sub, which isn’t tied to the BOM or DOM. I can easily use it to notify any component of any event, and what’s more, I do use it in this way. A built-in DOM-node-only implementation doesn’t look too attractive to me now.
  • The panel’s still out on the best way to do addEvent(). I’ll keep an eye on what they’re doing, of course, and reserve the right to modify my opinion in the future.

One Response to “On Event Handlers”

  1. AJAX Says:

    On Event Handlers

    Dave writes “The JavaScript Browser-Object Model (i.e. the bits of JavaScript that aren’t core language, but glue the language to the web browser) bears many legacies of the over-rapid development of features, bells and whistles from the late …