getting a this. with addEventListener and attachEvent
When writing script for asp.net controls, it's common to want to attach event handlers via script to elements. For some time now, I've been playing with the various ways to do this in order to find the “Best Way”.
My list of things I wanted to acheive were:
- I wanted to have “this.” to refer to the element which has caused the event, such as the button being clicked.
- I wanted it to be non-destructive. I wanted to make sure that nobody else's handlers where removed when I added mine.
- I wanted it to work on v4-era + browsers.
As of last nite around 2am, I'm fairly satisfied that the following method is pretty much the best that's going to happen.
function XBrowserAddHandler(target,eventName,handlerName) {
if ( target.addEventListener ) {
target.addEventListener(eventName, function(e){target[handlerName](e);}, false);
} else if ( target.attachEvent ) {
target.attachEvent("on" + eventName, function(e){target[handlerName](e);});
} else {
var originalHandler = target["on" + eventName];
if ( originalHandler ) {
target["on" + eventName] = function(e){originalHandler(e);target[handlerName](e);};
} else {
target["on" + eventName] = target[handlerName];
}
}
}
Here's an example of how it would be used. (The following code assumes that a couple buttons exist on the form, with an appropriate Array-Declaration with their names, and a call to the Init method.
function Demo_Init() {
var theForm = document.forms[0];
for( var i = 0; i < Demo_Buttons.length; i++ ) {
var theButton = theForm[Demo_Buttons[i]];
theButton.MyMessage = "This Is The Script-Attached Message On " + Demo_Buttons[i];
theButton.ClickHandler = Demo_ClickHandler;
XBrowserAddHandler(theButton,"click","ClickHandler");
}
}
function Demo_ClickHandler(e) { alert( this.MyMessage ); }
using the XBrowserAddHandler method, it doesn't matter if the element declares it's own onclick attribute, everything still runs as expected.
As for my requirements: #1 is satisfied by making the handler a method on the element itself. This usually doesn't play well with the addEventListener/attachEvent stuff, so lets look at why it works.
target.addEventListener(eventName, function(e){target[handlerName](e);}, false);instead of adding the function itself, i actually declare an anonymous function as the real handler, and this function calls the intended handler thru the given “target” argument, preserving “this”. For those of you coming from a SmallTalk or LISP background, you may be able to correct me here, but the feature being used here is called a “Closure”, because the local args “target”, and “handlerName” are actually kept around for the time that the anonymous event handler function is called, long after they go out of the scope of XCrossBrowserAddHandler. It's some pretty neat stuff, and shows how javascript is one of the coolest languages ever. It's OO, it's Functional, it's Procedural. Whatever you need, it can do it.