A Good Enough addEvent

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.

73 Comments

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

  2. 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. @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. 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:

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

  5. 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. 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. 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. 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. 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.

  10. I’ve actually been working on this “problem” of getting a good add/remove event toolset (not framework based).

    Mine is admittedly a bit longer in terms of code and less “graceful” looking, but I think it’s got coverage for a lot more of the “problems” that people complain about with event handler adding/removing.

    The strategy I used to be able to chain any number of handlers for an event, even duplicate ones, and have the ordering and remove-ability preserved, was using a function closure chain.

    Here’s the claims that it makes:
    1. Preserves the “this” in handlers to refer to the object the event was fired on.
    2. Works on pretty much any browser/version (and doesn’t require any browser sniffing/detection to do so, since it falls back to the old-school onXXX style)
    3. Allows mutliple chained event handlers (even duplicate handlers), which execute in a predictable FIFO order, regardless of browser. But duplicate handlers are removed in LIFO order, as I think would be expected.
    4. does use expando properties (and closures) on the target object, but manages them in such a way that if an author properly calls unbindEvent() for their events when the page unloads, things should not leak (IE).

    Here’s also a working example of the code: http://www.getify.com/test-events.html.

    And here’s the code:

    var UNDEF = "undefined", JSFUNC = "function";

    function bindEvent(obj,eventName,handlerFunc) {
    eventName = eventName.toLowerCase();
    if (typeof obj[eventName] === JSFUNC) {
    (function(ename) { // recursive function to move the chain of signatures down one link to make room for the new handler
    if (typeof obj[ename+"_"] === JSFUNC) { // *next* item already a chain
    arguments.callee(ename+"_"); // recurse to keep shifting the chain links down
    }
    obj[ename+"_"] = obj[ename];
    })(eventName+"_");

    obj[eventName+"_"] = handlerFunc; // store handler signature for removal purposes
    var prev_chain = obj[eventName];
    obj[eventName] = function() { // insert new link into the chain
    prev_chain(); // 'recursively' (through closure wrap-up) call previous link/chain -- FIFO execution
    handlerFunc(); // call new link
    };
    }
    else { // just old-school register handler, and store its signature to create the first link of the chain
    obj[eventName+"_"] = (obj[eventName] = handlerFunc);
    }
    }

    function unbindEvent(obj,eventName,handlerFunc) {
    eventName = eventName.toLowerCase();
    if (typeof obj[eventName] === JSFUNC) {
    var last_ename = null, ename = eventName+"_"; // start looking for signatures at first "_" level hung off the end of the event name
    while (typeof obj[ename]!==UNDEF && obj[ename]!==null) { //loop through all signatures to find the target handler to remove
    if (obj[ename] === handlerFunc) { // target handler found, so remove its signature from the chain
    while (typeof obj[ename+"_"] === JSFUNC) { // keep going as long as the next higher link in the signature chain is defined
    obj[ename] = obj[ename+"_"]; // move the next higher link down one level to replace the existing link
    last_ename = ename; // used to keep track of which level/signature-name is the new highest one in the chain
    ename += "_"; // move up one link level in the signature chain
    }
    obj[ename] = null; // the last link of the chain needs to be null'd
    try { delete obj[ename]; } catch (err) { } // and deleted

    if (last_ename !== null) { // if any chain of signature links still exists, it needs to be re-wrapped into a ball of closure function calls
    var chain = obj[last_ename]; // start by capturing the function signature from the last link of the signature chain
    ename = last_ename.replace(/_$/,""); // start at the next to last element of the signature chain
    while (ename!==eventName) { // work backwards, rolling up the closures level by level, until you reach the beginning of the chain
    (function(){ // needs a "local scope block" for proper closure
    var prev_chain = chain, handlerFunc = obj[ename];
    chain = function() { // re-create the closure wrap-up function representing this link of the signature chain, save for next while-iteration
    prev_chain(); // FIFO execution
    handlerFunc();
    };
    })();
    ename = ename.replace(/_$/,""); // move back down one level of the signature chain
    }
    obj[ename] = chain; // entire closure chain re-created, so attach it to the main event handler property so it'll be called directly on event
    }
    break;
    }
    else { // target handler not found yet, step up one level of the signature chain
    last_ename = ename;
    ename += "_";
    }
    }
    if (typeof obj[eventName+"_"] === UNDEF || obj[eventName+"_"] === null) { // clean up -- if the signature chain is now empty (all events removed), remove the main event handler too.
    obj[eventName] = null;
    try { delete obj[eventName]; } catch (err) { }
    }
    }
    }

  11. It’s good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research. . . . . .

  12. That’s probably because PPK’s contest requirements were like asking for all three of good, fast, and cheap.

  13. I have a lot to be pleased to God for: mom and child’s wellness, epidural sedation, and buddies who were able to look at Sam while we were in the medical center. australia coupons

  14. This publish is very easy to study and appreciate without making any information out. Excellent work! bubblegumcasting.com reviews

  15. The technique I used to be able to sequence any variety of handlers for a meeting, even copy ones, and have the purchasing and remove-ability maintained, was using a operate closing sequence. Sound Cloud

  16. I’ve a lot being very happy to Our god for: mama and Houlteninstitute.com child’s wellness, epidural sedation, and friends exactly who could take a look at Mike although many of us were being in the medical center.

  17. This method We applied to be able to string just about any variety of handlers for a What is apple passbook? conference, actually content versions, and still have this acquiring and remove-ability preserved, ended up being having a function concluding string.

  18. However, that strategy is usually extremely ineffective, as the regex perform improves logarithmically. Unless a sed expert can factor out a better way, I’m going to proceed using the first strategy. Accountants London

  19. It really does not issue in my view. Both have pros and cons. It does not issue how you came to this world and what name your mother and father select Be yourself and let your own speech talk. check this out

  20. Whenever dimension is not so much of a problem, such as on the management part of a web page, I’m more likely to use a collection which will have a much more effective way of giving activities to things. galaxy s4 wallpaper

  21. This represents the screen item instead of the item on which you are linking the occasion, as in the situation of add Event Listener. artworks

  22. It was great to see the old printshop and everyone who works there again. I am excited to see a printing business still operating and growing, great job guys. What ever you think would be good

  23. That’s not a bad idea, Michael, but the problem is that wp_get_attachment_image() is a WordPress function; my plugin just filters some of the data it retrieves. So I can’t change the meaning or order of the function’s arguments. Hotel

  24. This web page is something that is required on the web, someone with a little creativity. useful job for providing something new to the world wide web. http://www.chandlersca.co.uk/

  25. I am excited to see a printing business still operating and growing, great job guys. cyprus company formation

  26. 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. business design

  27. I’m more likely to use a collection which will have a much more effective way of giving activities to things.) And my addEvent also creates this make reference to item in query, even for IE. Kratom Capsules

  28. Anyone notice Pauls phone doesnt have a cord? Its not a cordless. free cookbooks download ebooks

  29. It was great to see the old and everyone Have you printshop works there again. I am excited to see a printing business still operating and growing, great job guys. klik4d

  30. Kiss to the beautiful girl and the mommy and Sam and hugs to a Proud Daddy Austin. online flash games

  31. Factor that addEventListener will neglect it when you try to add the same owner to the same occasion on the same item. attachEvent does not. sportsbet

  32. 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. bulk glass pipes

  33. Of all the artist I enjoy, I always appreciate the realness and the soulful way she conveys a song. So looking forward to my new favorite CD. Keep up the great work and continued success. fun facts

  34. 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. snapchat

  35. The companies which provide these solutions, will provide you with non-automated or automatic likes and provide you with authentic Instagram likes. Tom Pisarski

  36. Thank you for honoring me like that ! Congratulations to Melita for having Margret like I had all my babies! It was hard but worth every minute of suffering. termografering pris

  37. WordPress function; my plugin just filters some of the data it retrieves. So I can’t change the meaning or order of the function’s arguments. noah health

  38. The disease. It’s hard to believe that polio, which afflicted FDR nearly a century ago, continues to p. warframeplatinumhack

  39. Till i could stop this virus, i want to automate some script to match and delete this patern or replace with some empty string. http://pieterkrijne.bloggertje.nl/note/47620/pieter-krijne.html

  40. Margret like I had all my babies! It was hard but worth every minute of suffering. prada handbags

  41. As electronic posting improves and more e-readers are presented to the market, we can anticipate to see more applications like Highlighter. Paul Mathieson

  42. “I want to use you, to work with you and through you in order for you to build my kingdom in heaven and serve others here on earth.” SEO

  43. Unless a sed expert can factor out a better way, I’m going to proceed using the first strategy. get redirected here

  44. It is apparently a dilemma using your information sites can you please recheck the idea. thanks once more. pdf books

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

  46. 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. Katie Brooks

  47. I’d do my female clone so hard we would fusionate into an androginous wonder, so beautiful and sexy the world would implode and god would have a wet dream. Onesies Australia

  48. Whenever dimension is not so much of a problem, such as on the management part of a web page, I’m more likely to use a collection which will have a much more effective way of giving activities to things.
    tips kecantikan

  49. This solution does suffice for most cases though, but then Scott Andrews. reference link

  50. 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. last minute travel deals

  51. Melissa Mast, Shipshewana, Ind., and Joseph Raber, Shipshewana, June 12, at Clinton Frame Mennonite Church, Goshen, Ind. new york criminal lawyer

  52. I loved your blog such a nice layout and knowledgeable article. Thank you for sharing.

  53. Thanks for sharing your views. Great blog here.. It’s hard to find quality writing like yours these days. I really appreciate people like you. I would like to thank for the efforts you have put in writing this blog. Visit
    switzerland tourist visa requirements

  54. 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. SEO

  55. The solution is to remove the height assignment from the container element, if you can. In my case, for another part of the site experiencing the same problem, I needed the element to have enough height to show a background image. The solution was to use the min-height property, which doesn’t trigger the bug. eFormula Evolution

  56. Something I skipped. Out of an almost $100 journey I did skip 1 $3 item… My bad. It was an sincere error and I am sure the 1st 2 time I did Simple Store I skipped.

  57. Something I skipped. Out of an almost $100 journey I did skip 1 $3 item. rumah dijual

  58. What I intended is: even when you make sites improved with JS behavior but don’t use any of the big frameworks/libraries such as jQuery or model you’ll usually have some kind of program with resources (a ‘toolbox’ of some sort) that you often use and that contains more than a general operate for including activities. My ‘toolbox’ contains f.i. a light-weight Ajax category, class-dealing features, cross-browser Range extra’s/generics etcetera. official sites

  59. Moron! You inrocks journalist, learns to understand the economics and politics before advancing such accusations. You inrocks journalist, when you are not controlled enough to talk about a subject learns to shut up! Agen Judi

  60. As we age this careful system changes. Moreover to monitoring moment-to-moment threats such as an beginning car or a decrease banister, our threat verifying starts to intuit a distant but progressively approaching dark thinking — the approaching end, the biggest boundary. ny bat removal

  61. They are definitely looking for new allows. They are not only there to offer assistance and responsibility, but the primary associates are providing back to us, too.” tax fraud lawyer

  62. To see and compare new items and technological innovation from a range of companies. pinterest

  63. A bit of pressure through the publish. Ripples improving across. official sites

  64. The Isles of the Southern Hawaiian come under the same going as all of small sized countries in the Earth Group.
    How will they take a position up on their own in a Globe that is going towards regionalism. official sites

  65. Equating assistance for the boycott with racial discrimination is a aggressive query at the. official sites

  66. The next essential activity can start. It’s a chance to get rid of the substrate from the jar and position it into the fruiting circumstances. Agen Togel

  67. They are definitely looking for new allows. They are not only there to offer assistance and responsibility, but the primary associates are providing back to us, too.Affiliate Marketing

  68. It’s good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research. remy indian hair weave

  69. 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.http://www.kizi.uk.com

  70. 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.freediabeticdietrecipes.com

  71. It’s good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research.upstheante

  72. It’s good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research.turbo taxes

  73. They are definitely looking for new allows. They are not only there to offer assistance and responsibility, but the primary associates are providing back to us, too brazilian hair cheap

Post a Comment

Your email is never shared. Required fields are marked *

*
*

6 Trackbacks