Event delegation in JavaScript
Last week, I spoke at and attended the Velocity web performance conference in San Jose. It was a lot of fun and I learned a lot in the other sessions I sat in on. During one session, Steve Souders announced to everyone that I had covered event delegation in my chapter from his new book, Even Faster Web Sites. Unfortunately, Steve misspoke as that topic isn’t covered, so I’m going to fill in the gap with this post.
Traditional event handling
Event delegation is, quite simply, using a single event handler to manage a particular type of event for the entire page. This isn’t a new idea, but it is an important one to grasp for performance. Most of the time, you’ll see code like this in web applications:
document.getElementById("help-btn").onclick = function(event){
openHelp();
};
document.getElementById("save-btn").onclick = function(event){
saveDocument();
};
document.getElementById("undo-btn").onclick = function(event){
undoChanges();
};
This traditional way of coding assigns an event handler to each element that is actionable. For web sites with only a small amount of interaction, this may be okay. Large web applications, however, can grind to a halt when there are too many event handlers. The problem here isn’t necessarily a speed issue but rather a memory issue. If there are hundreds of possible interactions, there will end up being hundreds of ties between DOM elements and your JavaScript code. The more memory required of your web application, the slower it will run in general. Event delegation helps by minimizing this issue.
Event bubbling and capturing
Event delegation would be possible if not for the flowing nature of events. Early on in web development, browser vendors had to answer a philosophical question: when you click an area on a web page, what element are you actually interacting with? The problem came with the definition of interaction. Clicking within the bounds of an element is somewhat ambiguous. After all, a click on any element is also within the bounds of other elements. Consider clicking on a button. You’re actually clicking within the bounds of the button element, within the bounds of the <body> element, and within the bounds of the <html> element (see figure below).
At the time of this problem, there were two dominant browsers: Netscape Navigator and Internet Explorer. Each decided to solve this problem in a different way. Netscape defined an approach called event capturing, where events first occur on the highest object in the DOM tree (document) and then work down to the deepest element affected by the event. So in this example, event capturing has the click event first handled by document, then the <html> element, then <body>, and finally the <button> element.
Internet Explorer approached the problem in the exact opposite way. The IE team defined an approach called event bubbling. Event bubbling said that the deepest element affected by the event should receive the event first, then its parent should receive the event, and then it’s parent, etc., until the document object finally receives the event. Even though the document doesn’t have a distinct visual representation separate from <html>, it is still deemed to be its parent and thus bubbling continues up the DOM structure. The previous example would then see the <button> element receiving the event first, then <body>, <html>, and finally, document.
When defining the DOM, the W3C apparently found merit in both approaches and so the DOM Level 2 Events specification defines both event capturing and event bubbling as being present. First, the document receives the event, then the capturing phase commences to the most specific element affected by the event. Once the event is handled by that element, it bubbles back up to the document. The DOM addEventListener() method accepts three arguments: the name of the event to handle, a function to execute as the handler, and a Boolean set to true to handle the event during the capturing phase or false to handle during the bubbling phase. Most web developers have always been told to provide false as this argument so that it behaves the same way as attachEvent() in IE. Example:
//bubbling phase handler
document.addEventListener("click", handleClick, false);
//capturing phase handler
document.addEventListener("click", handleClick, true);
Attaching an event handler via a property (element.onclick = function(){}), automatically assumes you want to use the bubbling phase to handle the event (this is done for backwards compatibility). Pretty much every browser except Internet Explorer (even through version 8.0) supports the DOM Level 2 events specification and therefore supports both capturing and bubbling. Internet Explorer still has its own proprietary event system that supports just bubbling.
Event delegation using bubbling
The key to event delegation is to use the bubbling aspect of events to handle them at the highest level (usually document). Not all events bubble, but mouse and keyboard events do, and fortunately, those are the ones you’re in which you’re interested. Revisiting the earlier example, you can handle all of the click events by assigning an event handler to the document and then checking the event’s target to determine the course of action.
document.onclick = function(event){//IE doesn't pass in the event objectevent = event || window.event;//IE uses srcElement as the targetvar target = event.target || event.srcElement; switch(target.id){ case "help-btn": openHelp(); break; case "save-btn": saveDocument(); break; case "undo-btn": undoChanges(); break; //others? } };
Using event delegation, the number of functions necessary to manage events has been cut back to one. All click events are now handled by a single function which then delegates to the appropriate function depending on the target of the event. The same can be down for mousedown, mouseup, mousemove, mouseover, mouseout, dblclick, keyup, keydown, and keypress. A word of caution, though, mouseover and mouseout are difficult to handle via event delegation due to their nature (the mouse is considered “out” when it moves from a container to its child).
Note: You can also accomplish event delegation via event capturing, but it only works in browsers that support capturing and therefore not in Internet Explorer.
Benefits
Event delegation has several benefits to the performance of a web application:
- Fewer functions to manage.
- Takes up less memory.
- Fewer ties between your code and the DOM.
- Don’t need to worry about removing event handlers when changing the DOM via
innerHTML.
Moving from traditional event handling to event delegation has improved the overall performance of large-scale web applications around the world. It’s become so important that JavaScript libraries such as YUI and jQuery have started to bake it into their core interfaces. It really takes very little effort to implement event delegation, but the performance gains can be quite noticeable through the user interface. This is especially apparent when you move from dozens of event handlers to just one. Give event delegation a try and you may just never do traditional event handling again.
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of Yahoo!, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
You can leave a response, or trackback from your own site.






26 Comments
[...] JavaScript – Event delegation in JavaScript – Tweeted by Mike Amundsen [...]
The Technology Post for June 30th, 2009 | Nexo IT - Information Technology News on July 1st, 2009 at 12:08 am
[...] Event delegation in JavaScript [...]
Recent Reading (HTML5, CSS Fonts, JavaScript, rel=nofollow, Google Analytics) » HTML + CSS + JavaScript » Blog Archive on July 1st, 2009 at 10:23 am
Once again, agreat and clear explanation.
Thanks for all your writing about Javascript programming, I absolutely love your JavaScript for Web developers book and I constantly refer to it.
I’m still a newbie but you’re making learning JavaScript a lot easier!
Natalie on July 2nd, 2009 at 4:18 pm
Thanks Natalie, I’m glad you enjoyed the post and the book.
Nicholas C. Zakas on July 3rd, 2009 at 12:12 am
Nice article Nick. Didn’t realise there was a visible performance benefit in this type of approach.
Presumably, this technique could also be used dynamically (ie adding handlers on the fly, via an array/object)?
Richy on July 3rd, 2009 at 10:28 am
@Richy - Yes, this works dynamically as well as statically.
Nicholas C. Zakas on July 4th, 2009 at 1:34 pm
Hah! Funny. That was what I was supposed to have written. Guess when I bailed on the book he had you mentally doing this chapter (I had suggested that)
Nice writeup.
Bill Scott on July 4th, 2009 at 5:41 pm
@Bill - So I guess it’s actually your fault that this misunderstanding occurred.
Actually, Steve did ask if I could write up something on event delegation for the book, but there just wasn’t enough time at that point in the process. I guess he forgot that it didn’t quite make it in. 
Nicholas C. Zakas on July 5th, 2009 at 1:29 pm
Nice article.!
how these events are handled in firefox browser.? is it behaves same as Netscape..?
Manikandan on July 11th, 2009 at 2:53 am
@Manikandan - Firefox supports the DOM event flow.
Nicholas C. Zakas on July 11th, 2009 at 2:06 pm
Not to rain on your parade, but.. there is a very important drawback to event delegation: unless it’s used very carefully it can dramatically reduce keyboard accessibility of a web application.
Many browsers and assistive technology software have features that let you jump between “activatable” elements, and use enter or similar to fake an activation. For example, Opera’s spatial navigation with shift-arrow keys can highlight anchors, buttons, any element with onclick listeners, and pressing enter will fake a click event on the focused element.
When the event handling takes place at the document level, we loose the granularity that enables detecting “activatable” elements, and keyboard accessibility becomes impossible.
If you implement event delegation, it’s thus VERY important to ensure you mark “activatable” elements in other ways. You should use proper A, INPUT and BUTTON elements instead of faking button- or link-styled P, DIV and SPAN tags, use attributes like tabindex and accesskey and in the semi-near future, ARIA attributes.
Hallvord R. M. Steen on July 17th, 2009 at 7:40 am
@Hallvord - If I’m understanding your point correctly, I’d say your major concern is that handling
clickevents at the document-level renders Opera’s spatial navigation inoperable due to its dependence on detectingonclickevent handlers. Here’s why I think it’s not a major concern: It’s a bad practice to have “activable” elements that don’t have some natural tab index in web applications. What you’re describing is more of a general usability/accessibility issue versus a problem with event delegation in general or Opera’s spatial navigation. The bottom line is that if you’ve developed your application in a way that is accessible, then adding event delegation will never hurt.Nicholas C. Zakas on July 18th, 2009 at 12:23 pm
I’m wondering if anybody has any hard stats of the memory and/or performance improvements in using event delegation. I develop a pretty large web app, and for ~400 event listeners, using event delegation saves me about 7 MBs in IE7. But considering the total memory usage is ~260 MBs, it seems like a pretty small savings that’s not worth the trouble of implementing event delegation (I have more complicated DOM structure than just a list). Anybody have any insight in this to?
James on August 20th, 2009 at 1:32 pm
[...] a apresentação deste conceito já foi realizado por Peter Paul-Kock, Rob Cherny, Robert Nyman, Nicholas C. Zakas, Dan Webb, entre outros. Não vou me extender sobre a explicação das fases dos evento (captura e [...]
Event delegation no javascript « Atividades e Interesses on August 26th, 2009 at 9:30 pm
The last sentence in this article is so true! Unless it was something simple I was creating I will always use Event Delegation in the future… it also helps solve the IE6 event handler memory leak issue, as it ceases to be a problem with this approach.
RoryH on October 1st, 2009 at 9:12 am
[...] Reference: http://www.sitepoint.com/blogs/2008/07/23/javascript-event-delegation-is-easier-than-you-think/ http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ [...]
JavaScript Trials and Tribulations » Blog Archive » Practical Example of using Event Delegation with YUI (Part 1 of 2) on November 3rd, 2009 at 8:55 pm
[...] before newer content comes in, in order to avoid memory leaks in IE. These are situations where “event delegation” plays an important role for web [...]
Event Delegation With YUI 3 » Yahoo! User Interface Blog (YUIBlog) on November 13th, 2009 at 6:32 pm
[...] before newer content comes in, in order to avoid memory leaks in IE. These are situations where “event delegation” plays an important role for web [...]
Event Delegation With YUI 3 | Buddy's Blog on November 13th, 2009 at 6:59 pm
[...] most involving jQuery. (If you’re unfamiliar with the concept of event delegation, check out this post by Nicholas [...]
Event Delegation in jQuery and Performance (live vs bind vs other options) - ravelrumba on December 24th, 2009 at 7:17 pm
[...] 有些情况下,我们真想把这些模板单独就构建起来一个可重用的模块,因为实际的需求里,实在太多此类情况,那么可以考虑使用事件代理/委托了: [...]
阿阳的个人博客 » Blog Archive » JavaScript’s Data Template and Event Delegation on January 30th, 2010 at 11:09 am
[...] Event delegation in JavaScript [...]
如何在事件代理中正确使用 focus 和 blur 事件 | CssRainbow.cn on January 31st, 2010 at 12:23 am
Nicholas, that’s a very interesting article. But I don’t understand the first two benefits you listed:
1. Fewer functions to manage
I don’t get this. Using this approach, I have two functions to manage: The top-level capturing function and the delegate function. Using the traditional approach, it’s only one function. So what is the actual benefit?
2. Takes up less memory
To be honest I don’t know much about javascript and memory efficiency, but looking at what I stated above, two functions should take up more memory than one, or not?
I’d really appreciate a reply!
Bernhold on April 1st, 2010 at 9:27 am
Bernhold - with you have one function to capture events for all objects on the page, rather than one for every element - all your
document.getElementById("foo").onclick = function(event){bar()}lines can be deleted.Mr Z - should the 4th word in
/html/body/div/div[2]/div/div/div[2]/p[4]be “not” and do you have an extra “you’re” in/html/body/div/div[2]/div/div/div[2]/p[11]?Nick Tulett on May 19th, 2010 at 2:59 am
Typical - post a proofreading comment and don’t check my own writing till it’s too late -
First line should be :
With event delegation you have one function…
Nick Tulett on May 19th, 2010 at 3:01 am
[...] http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ blog comments powered by Disqus var disqus_url = ‘http://meshfields.de/blog/?p=421 ‘; var [...]
Meshfields Blog » Event Delegation in JavaScript on June 2nd, 2010 at 1:07 am
[...] 如果不太了解的朋友,可详细阅读:《Event delegation in JavaScript》,这里不再累述。 [...]
如何在事件代理中正确使用 focus 和 blur 事件 - 隐遁峰 on June 3rd, 2010 at 10:29 pm
Leave a Comment