Rock Solid addEvent()
In the aftermath of the addEvent() recoding contest, a winner was announced. It was also further discussed in the particleTree November issue with another method provided (I won’t discuss it since it’s copyrighted content and it’s for paying customers only ;)). However even with all the fuss going on, I’ve taken an innovative approach with some existing methods and combined them into a very powerful function. It also accomplishes these three core issues.
- Degrades on older browsers such as NS4 and IE5 mac
- The this keyword remains in-tact
- Avoids memory leaks in Microsoft Internet Explorer
Since none of this is entirely revolutionary or actually originally written by me, I’m not going to take any further credit other than piecing together what was already out there.
Introducing, my addEvent()
Here for your copy and pasting pleasure, it consists of three parts. 1) The addEvent() function itself which programatically adds to the 2) EventCache (originally written by Mark Wubben), and 3) an unload event is added to the window to run the EventCache ‘flush’ method.
function addEvent(): under CC-GNU LGPL license
function addEvent( obj, type, fn ) {
if (obj.addEventListener) {
obj.addEventListener( type, fn, false );
EventCache.add(obj, type, fn);
}
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
EventCache.add(obj, type, fn);
}
else {
obj["on"+type] = obj["e"+type+fn];
}
}
var EventCache = function(){
var listEvents = [];
return {
listEvents : listEvents,
add : function(node, sEventName, fHandler){
listEvents.push(arguments);
},
flush : function(){
var i, item;
for(i = listEvents.length - 1; i >= 0; i = i - 1){
item = listEvents[i];
if(item[0].removeEventListener){
item[0].removeEventListener(item[1], item[2], item[3]);
};
if(item[1].substring(0, 2) != "on"){
item[1] = "on" + item[1];
};
if(item[0].detachEvent){
item[0].detachEvent(item[1], item[2]);
};
item[0][item[1]] = null;
};
}
};
}();
addEvent(window,'unload',EventCache.flush);
Place this in common.js as a must have tool and you’ll be equipped for years to come. Eventually we can ditch the support for NS4 and IE5 (some already have), but the IE6 support is no doubt going to stick around for quite a few more years.













November 29th, 2005 at 9:12 am
You’ll also want to use EventCache for Mozilla, as per this bugreport.
Also, could you add the CC-GNU LGPL license to this code snippet? Thats the license the Event Cache script is released under.
November 29th, 2005 at 11:46 am
No problem mark! And thanks for stopping by. As I recall, the last time you were here was quite some time ago. Some how, some way or another, every time I mention somebody here, they always end up dropping by.
I’ll modify the above functions and add the reference to the lisence. I was unaware of the extra leak from mozilla.
November 29th, 2005 at 2:42 pm
Yes, well, what can I say? ;-)
December 1st, 2005 at 3:55 am
With this method, I was under the impression there will still be memory leaks if elements (which have have event handlers set) are removed prior to the window being unloaded?
December 2nd, 2005 at 9:44 am
Sam, not really, no. The elements are still referenced, so removing them won’t clear up memory, but the events are also removed.
Or are you saying that the events won’t be removed because the element is no longer part of the document? I wouldn’t know about that, do you have some resource for this?
December 8th, 2005 at 5:50 am
In 3rd branch right part seems to be undefined.
else {
obj["on"+type] = obj["e"+type+fn];
}
Moreover, if you want it “rock solid”, you can use an approach from mentioned “function addLoadEvent”
else if (typeof obj['on'+type] != 'function') {
obj['on'+type] = fn;
}
else {
var oldonload = obj['on'+type];
obj['on'+type] = function() {
oldonload();
fn();
}
}
What do you think?
December 9th, 2005 at 1:05 pm
This is very useful, thank you very much.
I’ve added a removeEvent function and in doing so I noticed that IE wasn’t really removing the events using the code from EventCache.flush().
Instead of calling
I used
where eventtype is the unmodifed item[1] (without the “on” in front of the event), which seems to work properly.
Here is the function the way I’m using it:
Konstantin Pelepelin’s idea sounds good to me I may need to add that in as well.
December 13th, 2005 at 11:31 pm
[…] form> Function Prerequisites First you’ll need a copy of addEvent. It doesn’t have to be the one I put together as t […]
December 21st, 2005 at 7:25 pm
I dont get why you are
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
EventCache.add(obj, type, fn);
}
I mean it will work if you just use obj.attachEvent( “on”+type,fn );
without all that obj garbage
December 21st, 2005 at 7:59 pm
mario. that preserves the ‘this’ keyword when using addEvent on the attached event. That’s its sole purpose
December 23rd, 2005 at 11:49 am
Dustin, as far as I can tell (and from a quick test I ran) this does not prevent the default action for events in Gecko browsers like Firefox. For example, if you have a link with a default href attribute for non-js users, assigning a function as the onclick event handler for that link will not stop Firefox from navigating to that default page. Apart from putting in something like
if (e.preventDefault) {
e.preventDefault();
}
inside every event handling function, is there a better way of doing this?
P.S. you need to add in a <pre> button for all these poor people who post multi-line code ;) since the tags do work very nicely.
December 23rd, 2005 at 12:57 pm
[…] ipt) /* used in conjuction with both the dollar $() function and addEvent */ // addEvent() found @ http://www.dustindiaz.com/rock-solid-addevent/ // $() found @ http://www.dustindiaz.com/top-ten-javascript var togBar = { bar : null, nest : null, […]
December 29th, 2005 at 11:12 am
Thank you for your responce Dustin,
That method seems to cluttered. I am not sure I see what you mean by defining of ‘this’.
I know that the ‘this’ keyword is a tricky bugger and a lot of times people forget which scope they are in when using the ‘this’ keyword. Is their an example you can give were using the ‘this’ keyword would cause a problem in the way I choose to use it (without the xtra hoops to jump through).
December 30th, 2005 at 1:20 pm
Hi Mario,
an example of ‘this’ being used correctly is when attaching events to a particular element object. With this example of addEvent it preserves it to when you reference the object from the function that is attached to it.
For example if you attach the ‘click’ event to a link, and make it fire off a function called
showTitle(), and within the function you writealert(this.title), The ‘this’ keyword is referencing the link on which you’ve clicked….and with otheraddEventfunctions, ‘this’ will reference the window object - and not the link you clicked on - in Internet Explorer. Got it?January 4th, 2006 at 12:31 pm
having the function arugment being passed as a string can help with the ‘this’ problem and not pollute the dom:
//this is just a simple innerds to show ya what I mean
addEvent = function(obj,type,fn)
{
var f = ((typeof fn == ’string’)? function(){eval(fn);} : fn);
obj.attachEvent(’on’+type, f);
}
February 13th, 2006 at 1:58 am
Hi Guys,
This is all great and well, but I’m no javascript guru, so an example of how to use this lovely function would be appreciated. Also, I see there have been some recommendations made ~ has the script at the top of the page been modified to accommodate for this, or is it the old one? ~ like the Mozilla “leak” or whatever…
Thanks,
February 13th, 2006 at 3:58 am
Another thing, how do you check what the target is?
February 19th, 2006 at 2:24 am
[…] Assuming you have an addEvent() function handy, this is a simple way to get things started. You’ll notice at the bottom the inializer which fires upon the window ‘load’ event which will in return fire any init() methods we have from different Objects. Then within the ‘obj’ Object literal, there’s a, b, c, and d which all represent possible values for these name value pairs. […]
February 21st, 2006 at 5:48 pm
[…] If you’re wondering how to collapse any given amount of particular elements, you can simply use this function in combination with the prototype $() dollar function and addEvent: […]
March 1st, 2006 at 3:20 pm
[…] Furthermore, when I say forget addEvent, I mean any and every version of addEvent you can think of. This goes for Scott Andrew’s original addEvent function, every entry from the addEvent recoding contest, even Prototype’s Event observer and its many properties and extensions. I even had a stab at it myself and was pretty impressed with the outcome. […]
March 8th, 2006 at 8:34 am
[…] According to Dustin Diaz Yahoo’s new Event Utility is “is the dopest, sweetest, most tight, most sexiest event utility on the planet”. Furthermore, when I say “forget addEvent”, I mean any and every version of addEvent you can think of. This goes for Scott Andrew’s original addEvent function, every entry from the addEvent recoding contest, even Prototype’s Event observer and its many properties and extensions. I even had a stab at it myself and was pretty impressed with the outcome. […]
March 12th, 2006 at 9:29 pm
According to Drip, Allen’s removeEvent still leaks in IE. The following lines leave properties in the object. I’ve tried several things (like setting values to null and using the delete statement), but I can’t seem to get Drip to sign off.
obj[’e’+type+fn] = fn;
obj[type+fn] = function() { obj[’e’+type+fn]( window.event ); }
I was concerned about leaving the cleanup to the flush because I’m going to be calling addEvent on dynamic objects that get added and deleted constantly. Plus, users will stay on a single page for a long time, so I was worried about this gradual memory leak.
Anybody have suggestions?
May 19th, 2006 at 4:39 am
[…] Surely a staple to event attachment! Regardless to what version you use written by whatever developer, it does what it says it does. And of course as you might of known, I’ve put together quite a handy version myself recently of addEvent() with some help from the contest winner and Mark Wubben along with a few minor syntax adjustments. But just to be fair to Scott Andrew, here is the original that started it all. […]
October 14th, 2006 at 3:42 pm
I’m putting this to use on a client’s site, and it works like a charm. Thanks, Dustin!
November 8th, 2006 at 8:30 am
Hi!
This code is under the LGPL Licence. I’d just like to check that I have your permission to use it on a commercial website, and have the floowing four freedoms, as per http://creativecommons.org/licenses/LGPL/2.1/
The freedom to run the program for any purpose.
The freedom to study how the program works and adapt it to your needs.
The freedom to redistribute copies so you can help your neighbour.
The freedom to improve the program and release your improvements to the public, so that the whole community benefits.
Many Thanks,
Josh.
November 27th, 2006 at 4:38 am
How I see, new code examples are not welcome ,(
January 5th, 2007 at 5:45 am
Hi Dustin,
Thanks for your work here! Am just using this for a personal project and it looks like IE 5.0 is tripping up on the line ‘listEvents.push(arguments);’ saying that ‘Object does not support this property or method’. Any thoughts? Maybe I am getting it wrong :S
Or maybe the yahoo libraries are where it’s at right now?
Thanks in advance,
Alex
January 17th, 2007 at 7:12 pm
Hi everyone,
One more go at the this problem. Personally I find the solution from Dustin & co a bit elaborate, just as Mark did. Off course I have my own version of this, and reading the comments I couldn’t help but think I solved this ages ago. After a quick look, I found this:
There you go, even formatted with same variable names as Dustin’s version!! Works as expected, and is much nicer to see if you ask me. It works in IE 6, but I don’t see why lower/higher versions shouldn’t do it as well. If I’m correct, I made this on a version 5.5.
Aaaaah, that warm nice feeling of helping people out :)
January 17th, 2007 at 7:13 pm
Aye, twas not Mark but Mario. Sorry dude ;)
January 17th, 2007 at 7:20 pm
Oh and to Mark:
Just call from the event handler function.
January 17th, 2007 at 7:24 pm
Grrrrr, even postable doesn’t help in all cases. Where did the ‘+’ go between “on” and type? Or did I bork up?
So should be: obj.attachEvent( “on”+type, func );
And that’s the last post of today :)
January 26th, 2007 at 9:10 pm
Is this a test. Yes, I believe it is.
January 26th, 2007 at 9:28 pm
Dustin,
I have, what I feel, is a valuable comment to add to this page, unfortunately, whenever I submit a reply, my comment simply does not show up. Since I was having no luck submitting my post, I figured I would email you and let you know. No luck finding that email address. Alas, you have a contact page (http://www.dustindiaz.com/contact/)! It redirects to the front page. I know, I’ll check the sitemap! Same problem. So, I apologize for littering the comments of this page, but, what are the limitations I could possible be running into when trying to submit a comment on this page? My reply has JS and HTML and before you ask, yes, I used the pre & code tags and yes, I used postable to transform my content before attempting to post it.
March 7th, 2007 at 2:31 am
[…] With the exception of addEvent we also don’t currently use any third-party AJAX or JavaScript libraries. To date none of the AJAX functionality we’ve implemented has required the complex behaviours included in these libraries, so we’ve been able to get away with rolling our own simple asynchronous post/call-back logic. […]
March 28th, 2007 at 6:41 am
[…] Surely a staple to event attachment! Regardless to what version you use written by whatever developer, it does what it says it does. And of course as you might of known, I’ve put together quite a handy version myself recently of addEvent() with some help from the contest winner and Mark Wubben along with a few minor syntax adjustments. But just to be fair to Scott Andrew, here is the original that started it all. […]
May 28th, 2007 at 7:21 am
Awesome script! Ive been on the web looking for something like this for a while now and this is the easiest and the best one ive come across and the only thing i know about javascript is how to spell it, so thanks!
May 28th, 2007 at 7:24 am
Oh sorry, i posted this in the wrong window, i meant for the expand/collapse script as a whole!
August 16th, 2007 at 12:18 pm
This is so good. Finally something that works.
October 21st, 2007 at 9:47 pm
This code is precisely what I am looking for; however, I am running into a problem (or two…or three).
I am initiating the code in as an external javascript file. On page load, I receive the following errors:
IE7 throws errors:
expected “;”
then
conditional compilation is turned off
Debugging points to line:
FF2 & NN9 throw error “invalid label”, and both Firebug and NN Error Console point to the same line of code above.
Any ideas about what’s going on here? I suspect it is an ID-10-T / operator error. :-)
I did not see any explicit examples of how to add an event, but I did see how to flush the cache. Based on that example, I think this is the proper syntax:
If not, please straighten me out. The same error occurs when only the EventCache.flush event is listed.
October 22nd, 2007 at 5:48 am
I’ve done some research on the javascript “invalid label” error. Interestingly enough, all the information I find relates to JSON. All seem to point to the need to add an additional set of parenthesis “(” and “)” around the code particularly when code like “someLabel : ’some code’” is used.
I have played around with this trying to find where to include the additional set of parenthesis, but have not had any success. I thought that the “();” at the end of the “var EventCache = function()” code took care of this problem, but I guess not.
I will keep researching, but any information anyone might be able to provide would be greatly appreciated.
By the way, I have tested on both WinVista and WinXP SP2, and the problem occurs on both OSes.
October 22nd, 2007 at 6:52 pm
Well, I suppose if I keep posting enough, I will eventually solve the problem myself. =D
The problem, as it turns out, was due to my having “broken out the code” into a “more readable format”. More readable for me, perhaps, but not javascript. In var EventCache I had split the brace after “return” onto a new line; js did not like that. So now it is working.
Additionally, I figured out (duh!) the format for adding events:
[code=javascript]
addEvent(window, “load”, myFunction);
[/code]
For those of us (i.e. me) less savvy, we need explicit instructions, particularly when we start swimming in the deep end.
Finally, I still have a question. This is more a matter of preference, but I want to keep my code broken up into seperate files, which do not download to the client until called (on-demand javascript). So I do not want all my code in the same file as the addEvent code. How then do I use addEvent to load an external javascript? Apparently, it’s not as easy as pointing to the file. Do I need to use xmlhttprequest?
Thanks.
October 22nd, 2007 at 8:48 pm
Hi RMW:
You can call in code via xmlHTTPRequest, then eval your code. Or perhaps a better method would be to create new script elements, and set their src attribute. You could even run it through a function:
October 22nd, 2007 at 9:12 pm
Thanks Dustin! So is there a benefit to using the DOM method (your code) over the AJAX method (xmlhttprequest)? I know that I can use the DOM method to remove the script as well say when another is initiated. So many different ways of accomplishing the same task…sigh. =D
October 23rd, 2007 at 6:26 am
Dustin,
I’ve posted two messages now that have not shown up here. Both included code wrapped in the “pre” and “code” html entities. When I attempted to repost one, I was informed that I had “already said that”. :\
October 23rd, 2007 at 10:34 am
Hi RMW:
Yes, there are indeed many ways. It would definitely be a good idea to bring in code dynamically and then remove them as they aren’t needed anymore.
As for the twice posted, I’m not exactly sure what has happened. If you’re strictly talking about posting code, simply use ‘<pre>’ immediately followed by <code>, then within the body of code, run your code through the postable website (or simply just escape your code). The most forgotten messed up piece of code is within ‘for’ loops when someone uses the
<“less than” symbol… so I see a lot of this…When it should be this:
October 24th, 2007 at 7:38 am
I took your advice and wrote a script to create and remove “script”, “div”, “span”, and “img” elements; so thank you for that.
I have two final questions, and I swear I’ll leave you alone (but don’t hold me to it). =D
1. Does Rock Solid addEvent() call functions in the order they are listed?
or is it asynchronously loaded? The reason is, some of my functions are dependent upon others loading first–my loadDOM function mentioned above must load before my other scripts, which require DOM element creation. I am having trouble with my loadDOM functions not being able to be accessed by other functions if I call them with addEvent.
Which leads me to the second question…
2. Does addEvent() in any way effect how vars and functions are loaded into the global space? In other words, once a function is loaded using addEvent, I should be able to access it at any later time, correct?
Thanks for your time and responses!
October 24th, 2007 at 10:21 am
Hi RMW,
never count on your functions being run in the order you want when they’re assigned as event listeners. Their sole job is to listen, and be run when called. Nothing in the W3 DOM spec says that their order of execution is guaranteed. For example:
Try running this script in various browsers, see what color the element turns. Try even running it multiple times in the same browser… Then try expanding the what each of the functions do - then you’ll find that you end up running into a race condition, and the only way you can get guaranteed order of execution (for when it matters) is to pile up your logic into a single event listener (then again, only for the cases when it will matter).
cheers.
October 25th, 2007 at 6:11 am
See? Told you not to hold me to it. =D
So how about initiating each addEvent() in an ordered fashion…say with
Or something like that? I suppose the easist way of all is to have a single addEvent() “main” function, which calls the next addEvent() at the end, which calls the next…etc. A little clunky though.
Maybe there is a more eloquent way of ordering the addEvent()s firing off in a particular order. How could that be done? Like modifying function addEvent(obj, type, fn, order) where “order” = [a unique whole number] and including some code that fires each off starting with 0, waits for it to initiate, then fires the next?
Just thinking out loud here.
October 25th, 2007 at 7:16 pm
What about this:
Actually, I think there is a way to do multiple variables per value in the Array, like x(1), x(2), x(3)…instead of using quotes around the data. I saw something like that on a website today. I will have to go back an find it.
Anyway, what do you think?
December 1st, 2007 at 6:28 pm
[…] Get an addEvent function. There are literally hundreds. Pick one. I wrote one a few years back. Or even use an event utility. But really. It comes down to […]
December 8th, 2007 at 10:35 am
[…] Inspired by and/or based on the following: Aaron Moore’s addEvent() Dustin Diaz’s addEvent() […]