How JavaScript Event Delegation Works
One of the hot methodologies in the JavaScript world is event delegation, and for good reason. Event delegation allows you to avoid adding event listeners to specific nodes; instead, the event listener is added to one parent. That event listener analyzes bubbled events to find a match on child elements. The base concept is fairly simple but many people don't understand just how event delegation works. Let me explain the how event delegation works and provide pure JavaScript example of basic event delegation.
Let's say that we have a parent UL
element with several child elements:
<ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul>
Let's also say that something needs to happen when each child element is clicked. You could add a separate event listener to each individual LI
element, but what if LI
elements are frequently added and removed from the list? Adding and removing event listeners would be a nightmare, especially if addition and removal code is in different places within your app. The better solution is to add an event listener to the parent UL
element. But if you add the event listener to the parent, how will you know which element was clicked?
Simple: when the event bubbles up to the UL
element, you check the event object's target property to gain a reference to the actual clicked node. Here's a very basic JavaScript snippet which illustrates event delegation:
// Get the element, add a click listener... document.getElementById("parent-list").addEventListener("click", function(e) { // e.target is the clicked element! // If it was a list item if(e.target && e.target.nodeName == "LI") { // List item found! Output the ID! console.log("List item ", e.target.id.replace("post-", ""), " was clicked!"); } });
Start by adding a click
event listener to the parent element. When the event listener is triggered, check the event element to ensure it's the type of element to react to. If it is an LI
element, boom: we have what we need! If it's not an element that we want, the event can be ignored. This example is pretty simple -- UL
and LI
is a straight-forward comparison. Let's try something more difficult. Let's have a parent DIV with many children but all we care about is an A
tag with the classA
CSS class:
// Get the parent DIV, add click listener... document.getElementById("myDiv").addEventListener("click",function(e) { // e.target was the clicked element if (e.target && e.target.matches("a.classA")) { console.log("Anchor element clicked!"); } });
Using the Element.matches API, we can see if the element matches our desired target.
Since most developers use a JavaScript library for their DOM element and event handling, I recommend using the library's method of event delegation, as they are capable of advanced delegation and element identification.
Hopefully this helps you visually the concept behind event delegation and convinces you of delegation's power!
Good overview. I was just wondering could use:
Instead of the loop. I know it is not supported everywhere but everyone with
addEventListener
supportsindexOf
The availability of
Array.indexOf
is independent of any one method ornodeList
, it is dependent on the browser you are using and the version of JavaScript it implements. Firefox has hadindexOf
for some time now, so to with later version of Google Chrome.While
indexOf
isnt supported by some older browsers, a polyfill can be found at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOfI think it will be even better to use
RegExp
for class testing :)Yep, that would certainly work well too!
Why not
element.classList.contains(className)
?element.classList
dont work in any browser, look for: http://caniuse.com/#search=classlistI think in this way, we can use
classList
to detect a specific class on an element.If A has a
className
elementABC
,but what we want to find really is classelementA
, in this case, useindexOf('elementA')
is not correctNice post, the importance of event delegation can’t be hyped enough!
Good concept. When dealing with dynamic content that needed events, I typically loaded with a
rel
set to “untouched”, or without one at all and load a function that would look for all untouched methods or without the “touched” rel, give them the correct event, and set theirrel
‘s to “touched”.Is there a performance improvement done by using your method?
Depends on what you’re doing. It occurs to me, however, that you may need to run the event routine multiple times if you ever add elements dynamically. The rel tag solution, if you only want to do something once, isn’t a bad idea. Elitists would tell you to use data-[whateverAttributeName].
Good Example. Is there is any thing for Memoization.?
When
className
returns an empty string, split should return an array containing one empty string, which evaluates to true in an if statement. Otherwise split will return the split elements or an array with the original string. That being said, isif (classes)
cautionary, is it directed at a known browser, is it unnecessary, or am I missing a case where the if would be false? I can’t blame one for being cautious especially with browser reputation being what it is and can’t rule out that I’m missing something!I’m an experienced programmer, but relatively new to javascript, but not new to GUI programming (Windows, Swing, Android, etc.).
I read a lot about the importance of event delegation, but I’m skeptical. To me, it seems like it’s a venerated “best practice” that’s no longer relevant. I just can’t believe that modern computers and browsers are affected by the very marginal gains (?) offered by event delegation. What exactly *are* the benefits?
From a coding style perspective, event delegation shows odd cohesion.When coding event handling in other environments (like desktop, etc.) one normally doesn’t have a central event handler with an embedded case structure to test what was clicked. Especially with the common handler tied to some UI implementation which could very well change as users ask for changes to the UI. User: “Please move that button on the toolbar.” Of course, event handlers on the individual items would mean such changes are not problematic.
The author says “what if LI elements are frequently added and removed from the list? Adding and removing event listeners would be a nightmare.” Ok, for *semantically* grouped items, like items in a list, I can see it (just like you’d have a single event handler for a combo box, and not on the items in the box).
So… Is this some venerated yet anachronistic style, or are there any statistics or tests I can run that show the benefits.
There are indeed direct benefits. In standard application programming, one may deal more directly with the garbage collector or at least have a better conception of how the GC operates. In web development, the GC is completely variable per user agent — you have literally no idea how or when it will operate because each user may potentially use a different agent.
Imagine a use case where you have a grid of several hundred dynamically generated table cells (think an hour-by-hour availability calendar): attaching an event to each of these cells would, for starters, take a moment. While you’re looping over them, the browser UI thread is locked and the user perceives bad performance. You also wind up with several hundred listeners, and depending on your script architecture that could also mean several hundred function references. This is in itself weighty… but now the user switches dates and you have a new batch of cells injected into the table. Do you loop the old cells and remove the listener from each? That will take time, then you have to loop again to apply the new listeners. Do you just ignore the old and only worry about the new? Now you have several hundred references sitting in memory, waiting for the mythical GC that will fire who-knows-when. Plus, you may have created circular references and those listeners will never leave memory, *even when the user navigates to a completely different page*. Your function references are still sitting in memory, too. That is one quite feasible use-case where a single event listener and delegation eliminates all those references and concerns.
In your toolbar button example, the delegating event would be attached to the toolbar itself. This pattern actually compliments a more flexible UI; you don’t think about the event, you just move the button. Or add a new one. Or delete one. No matter what change takes place, the single event listener will bubble up any actions.
I don’t think the cohesion is odd at all — one would use delegate events to apply an action to a specific group of elements. Cohesion already existed; these buttons are toolbar buttons, and when you click on, a centralized handler will determine what happens. This is how you already code (presumably), the real differences is that without delegation, you have more references to keep track of.
Delegation makes most sense when it is used with mutually cohesive elements:
LI
s in aUL
,TR
s in aTABLE
, etc. Control icons and the like are also another common use, though they aren’t semantically grouped they still have an underlying cohesion whether you used delegation or not.This is a really great explanation. Thank you for shedding more light on the gains. I had already begun adopting delegation just for the sake of avoiding the need to replace event handlers on dynamic elements, but the discussion of performance implications was really enlightening. Cheers!
hi, i try create a function same live, delegate, on of jquery, any idea?
Thanks for explaining this in very simple manner. Now I can take benefit of it in jquery OR all js libraries as well.
No need treat it as like magic.
Just for the record…
In jQuery you can implement it using CSS selectors like this:
It’s actually even easier than that! Using jQuery’s on the value of
this
in the callback is bound to the target element. So:this is not event delegation method this is direct method
Event Delegation:
This article is a very good example of event handle in JS. But it would be better if the author can talk more about the cross browser issue.
Good article but
i prefer to bind all events in custom event binder.
just give a JSON to event binder and boom.
Can you elaborate more on your custom event binder?
What is its benefits over the event delegation mentioned in this article?
The
target.nodeName
check fails if there’s an span tag inside theA
element, so thenodeName
returnsSPAN
and notA
. Is there an easy way to get around this?target.parentElement.nodename
you should go with
target.tagName
it will bring back only the target element not all his childs. Nice, informal article, btw.Hey – thanks a lot for this post! This solution will tidy up my implementation nicely.
Good explanation, it makes things clear
I liked the post… I used it as inspiration for my own article :)
http://robbypelssers.blogspot.nl/2014/04/javascript-event-delegation-bubbling.html
I think the className should handle like this:
for when there is more than one space in attribute class
I’m just wondering why you are checking if
e.target
exists, in which cases it wouldn’t?I guess you are right. It is not needed. Even the whole “if” condition in the first example is not needed when we want e.target to be all the list elements and not some selected ones (with “className” or “id”).
So, if you add
blur
orfocus
events oninput
.How to delegate its events for refresh new DOM events??
Hi, this is an old post but still up to date. I was looking for Event Delegation and this article was very useful. Thank you so much!
I just to add something that might be useful xD
You don’t need to distinguish if it’s a class, or any other valid css selector. You just have to compare the node instances… right?
Came here from react docs. Does react have an event listener at the top of the document, which is configured to listen to every built in as well as custom events? If so do all events get captured there in that listener?
Very well explained. It helped me to understand the concept of event delegation. Thank you.
I was trying to build some event delegation library and then I simply made this, enjoy! -> https://github.com/MarekZeman91/element-event-delegation
Can’t we add a class to all the li elements and then add the event listener on the class selector ?? Even if we have to add an element, we would add the class to the element before adding it.
So after reading this article and comments I wanted to ask which method should I use. Whether it would be event delegation or bubbling?
As one person gave an example let’s doctors appointment calendar, for which I think event delegation is best option but in general which one should be preferred?
I know it’s an old thread but it’s still relevant. The problem with this solution is that it only works if the target of the click is *precisely* the target of the event. For instance, suppose you want to observe clicks on all `<a>` tags on the page. If you click on a inside that `<a>` tag, the event target would be the thus not triggering the event.
There you trigger the event if the target is or is inside the CSS selector.
Hi, would this approach work with individual
id
s as well instead of classes? I have an exercise in DOM which requires me using only id’s.