A Good Enough addEvent

by filosofo. Posted on April 14, 2008 at 11:48 pm

Several years ago, PPK of Quirksmode sponsored a contest to come up with a new version of the trusty JavaScript addEvent function. The original addEvent was created by Scott Andrew LePera in 2001 as a way to merge Internet Explorer’s attachEvent with the W3C’s addEventListener. Both addEventListener and attachEvent allow you to attach a JavaScript event to a DOM object, but they differ in important ways. In particular, IE’s attachEvent doesn’t maintain the scope of the this keyword: this refers to the window object instead of the object on which you’re attaching the event, as in the case of addEventListener.

PPK’s contest itself ended up falling flat, as even the winner, John Resig (who later created the jQuery library), later repudiated it himself. That’s probably because PPK’s contest requirements were like asking for all three of good, fast, and cheap.

So seven years later, there’s no widely-adopted replacement to the original addEvent that:

  1. Is short
  2. Maintains the this scope in IE
  3. Has a corresponding removeEvent

The various libraries do a good job of 2 and 3, but not 1, and since I often find myself needing 1 and 2 but not 3, I came up with my own good-enough version of addEvent:

var addEvent = function( obj, type, fn ) {
        if (obj.addEventListener)
                obj.addEventListener(type, fn, false);
        else if (obj.attachEvent)
                obj.attachEvent('on' + type, function() { return fn.apply(obj, new Array(window.event));});
}
 

It’s short, which is just what I need when I’m trying to keep the JavaScript size low. (Whenever size isn’t so much of an issue, such as on the administrative side of a website, I’m more likely to use a library which will have a much more robust way of assigning events to objects.) And my addEvent also makes this refer to object in question, even for IE.

One Trackback/Pingback

  1. [...] days. But before it truly shuffles off into the sunset, allow me to point you to filosofo’s “good enough” addEvent. Austin gets around PPK’s pesky requirements by ignoring one of them (namely, having a [...]

9 Comments

  1. molily commented on April 16, 2008 at 7:32 pm | Permalink
    molily

    The slightly shorter fn.call(obj, window.event) should suffice.

  2. Tino Zijdel commented on April 17, 2008 at 7:24 am | Permalink
    Tino Zijdel

    One other point that this solution does not fix, since it is still a wrapper around MSIE’s propriety (and sub-optimal) eventmodel, is the fact that addEventListener will ignore it when you try to add the same handler to the same event on the same object. attachEvent doesn’t.

    This solution does suffice for most cases though, but then Scott Andrews original addEvent also worked for most apart from the scope-issue.

    In some cases however correctness is more important than brevity, and if you already include a library that’s several kilobytes in size than I prefer a solution that’s more correct and flexible than one that is only short and may backfire on me.

    And all things considered you can count yourself lucky if you don’t have to support browsers that support neither the W3C DOM2 eventmodel nor MSIE’s eventmodel ;)

  3. filosofo commented on April 17, 2008 at 3:14 pm | Permalink
    filosofo

    @molily: I don’t know why I didn’t think of that.

    @Tino Zijdel: It depends on what you mean by a library of “several” kilobytes. Most of the time I just want to run some code when the window has loaded or attach a click event, and then it’s “good enough”–no need to add kilobytes.

  4. Tino Zijdel commented on April 17, 2008 at 4:45 pm | Permalink
    Tino Zijdel

    What I meant is: even when you create websites enhanced with JS behaviour but don’t use any of the big frameworks/libraries such as jQuery or prototype you’ll usually have some sort of script with utilities (a ‘toolbox’ of some sort) that you often use and that contains more than a generic function for adding events. My ‘toolbox’ contains f.i. a light-weight Ajax class, class-dealing functions, cross-browser Array extra’s/generics etcetera.

    If you only want to add an eventhandler to some object why use this solution instead of just doing object.onclick=handler - that also ‘fixes’ the ‘this’-issue and by your own reasoning: how often do you find yourself needing to add multiple handlers to the same event on the same object? ;)

    If you insist on a generic function for that, here it is:

    [js]
    var addEvent = function( obj, type, fn ) {
    obj['on'+type] = fn;
    }
    [/js]

  5. filosofo commented on April 17, 2008 at 8:27 pm | Permalink
    filosofo

    I guess I have another, tacit criterion: I like to know that my script won’t interfere with or be interfered by other scripts.
    If you do something like the following

    window.onload = function1;
    window.onload = function2;

    function2 will override function1, but

    addEvent(window, 'load', function1 );
    addEvent(window, 'load', function2 );

    works fine.

  6. Dustin Diaz commented on April 17, 2008 at 11:54 pm | Permalink
    Dustin Diaz

    This is good and makes perfect sense. I also came up with a very short and concise addEvent in my article to write better JavaScript. It even does a simple branch so you’re not always detecting the object in question (W3 vs IE).
    All in all, you make a good case. And I too rarely ever use removeEvent… it seems like a waste of time for smaller websites :)

  7. Tino Zijdel commented on April 18, 2008 at 2:56 am | Permalink
    Tino Zijdel

    I think it’s a bad sign if you don’t know what other scripts are doing on your page (they just as well might overwrite your addEvent function!) ;)

    Besides that I personally never use window.onload; I have a DomLoaded class (in my toolbox) and register onload functions to that using DomLoaded.addLoadEvent()

  8. scottandrew commented on April 18, 2008 at 4:03 pm | Permalink
    scottandrew

    I think it’s a bad sign if you don’t know what other scripts are doing on your page

    Try working on a site like where multiple departments with multiple developers own discrete sections of a large website. It’s rare that one developer “owns” the window.onload event.

    Being able to overload event handlers safely is far more valuable than the ability to detach handler functions. If you’re attaching so many handlers that you have to detach them to prevent leakage, I’d say that’s a poor design to begin with.

  9. Tino Zijdel commented on April 18, 2008 at 5:50 pm | Permalink
    Tino Zijdel

    It’s rare that one developer “owns” the window.onload event.

    No-one should ‘own’ that event in such circumstances; you’ll need global conventions such as ‘onload events should be registered using this-and-such interface’.

    I was just trying to illustrate that even though it is good that you try to play nice, other scripts might not and can screw up yours (or each other) anyway.

    My more general ‘criticism’ is about the ‘good enough’ part of this article: I argued that for most people your original addEvent wrapper might still be ‘good enough’ and even just using object.onevent=function is ‘good enough’ in most cases. It just all depends on what you think is important and on what you actually need. You just need to be aware of the limitations of the solution you implement, and in my humble opinion most people are just not knowledgeable enough for that.

    In general I think that ‘brevity’ should not be such a heavy factor when evaluating cross-browser eventhandling solutions, especially not if you already include several KB of script for other generic purposes.

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

Subscribe without commenting.

Comment moderation is enabled. Your comment may take some time to appear.