Scott Andrew

Posted February 12, 2001.

Crossbrowser DOM Scripting: Event Handlers

You might think that with the advent of Netscape 6 and increasing standards support in Internet Explorer 5, crossbrowser scripting is a thing of the past. Guess again. There are still plenty of differences to code around between these two browsers. The good news is, instead of compensating for Netscape's weaknesses, we'll be playing to its strengths and coding around IE's proprietary implementations. This is not meant as a knock to Microsoft, who've put some very useful features into their browser. But the goal here is not to rely on proprietary code, but do the best we can to use the W3C's recommendations for the Document Object Model and its ECMAScript interface, and only branch the code when necessary.

The differences in event registration in NS6 and IE5 are a perfect example. As we saw in Working With Events in Netscape 6, NS6 implements the EventListener interface as described by the W3C spec. To attach an event to an element, you use that element's addEventListener method to define the type of event to listen for, the handler function to execute when the event is fired, and whether you want to use event capture:

object.addEventListener(String evType, Function fn, Boolean useCapture);

To illustrate, this is how you could attach an onmousedown event handler named processEvent to an image object in NS6:

imgObj.addEventListener("mousedown", processEvent, false);

To remove the handler, you simply call the element's removeEventListener method using the exact same arguments:

imgObj.removeEventListener("mousedown", processEvent, false);

IE5 does not employ the EventLister interface. However, it has a similar set of methods, attachEvent and detachEvent, which take similar arguments. Here is how we would attach the same event to the same object in IE:

imgObj.attachEvent("onmousedown", processEvent);

Immediately we notice two things:

It should be noted that Microsoft was an early implementer of many W3C working drafts and recommendations, so it may be that attachEvent was an attempt to implement the EventLister interface prior to its formalization.

With this information, its not hard to see how we can get around these differences with a bit of creative scripting. Let's make our own event interface!

We'll start by creating a function to replace the IE5 and NS6 implementation. We'll call it addEvent and design it to accept four parameters:

function addEvent(obj, evType, fn, useCapture){

}

Now, rather than do any browser sniffing, we'll first just check to see if the object implements the EventListener interface. If so, we'll just use that and call it a day. This will keep our code compatible if IE implements the same interface in the future.

function addEvent(obj, evType, fn, useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evType,fn,useCapture);
    return true;
  }
}

The return true; statement is simply to provide a return value in the case of NS6. addEventListener does not return a value by itself, but IE's attachEvent returns either true or false depending on whether or not the operation was successful.

If addEventListener is not present, we'll assume IE5 and check for the attachEvent instead.

function addEvent(obj, evType, fn, useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be attached");
  }
}

Note how I'm appending "on" to the front of evType, and discarding the useCapture boolean, which is only relevant to the EventListener interface. If the browser implements neither addEventListener nor attachEvent, the alert statement warns the developer of the problem.

To remove an event, we can easily build a removeEvent method that uses almost exactly the same code, but utilizing removeEventListener and detachEvent.

function removeEvent(obj, evType, fn, useCapture){
  if (obj.removeEventListener){
    obj.removeEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.detachEvent){
    var r = obj.detachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be removed");
  }
}

In just a few short lines of code, we've bridged the gap between the IE5 and NS6 event models. This code can be extended to include more error checking, or if you're really ambitious, backward compatibility with NS4/IE4. (You're on your own on that one.)

As support for standard scripting models increases, workarounds like this one will hopefully become unnecessary, which is why we should strive to use standards-based approaches in our web applications, and encourage browser manufacturers to implement standards before proprietary features.

Copyright © 2001 by Scott Andrew LePera