I think it’s wise to start with the “What”, then the “Why” and then finish off with the “How”.

What is it?

Event delegation is the concept of applying an event listener to the parent element of the element(s) that you’re really interested in. When you click a <div /> on a web page, the click event is registered with the DIV, then the event bubbles up through the DOM tree to the document. You would normally attach an event listener like so:

document.querySelector('#tweetBtn').addEventListener('click', function(){
	console.log('Consider me clicked');

So we’ve intercepted that bubbling up of the event and said, “no worries, I’ll handle it”. We catch the click event at it’s inception, as we’ve applied an event listener directly to the element itself.

This would also work, if we clicked the #tweetBtn element:

document.addEventListener('click', function(){
	console.log('Consider me also clicked');

This works because we’ve attached an event listener to the top most point of the DOM tree.


Imagine we have this HTML:

<ul class="anchormen">

And we want to attach an event listener to each of the <li /> elements inside our <ul/>. We could grab the UL and loop over the LIs inside, adding an event listener as we go. Or we could be smart and only attach one listener; to the parent. We could add an event listener to the UL and tell it to only respond if the original event came up from a child LI, if it bubbled up. How would this look? Like so:

document.querySelector('.anchormen').addEventListener('click', function(e){
    if(e.target.nodeName.toLowerCase() == "li"){
        console.log("I'm not even mad, I'm impressed!");

You can see that we’ve selected the UL from the DOM using querySelector, then we’ve checked the target of the event and seen if it’s nodeName value was equal to the string “li”. If it was, we’re in business. If it wasn’t, nothing happens. With this sample HTML, you can see we actually only have one active listener bound in our document, even though we’re looking out for 4 click events. As you might imagine, employing this way of attaching listeners can become very advantageous when you have a number of the same events, all under one common parent. Attach the listener to the parent, and you’re away.

If you’ve ever used jQuery’s $.ajax() or even XMLHTTPRequest to populate dynamic HTML into the DOM, then you’ll know that you have to re-apply event listeners to the new HTML, every time it’s inserted into the document. If you’ve never done that, just know that it’s tedious and annoying. Using event delegation means we eradicate the need of doing this. Instead of re-applying direct event listeners to dynamically inserted HTML, we attach one to the permanent parent element and tell it what element to look out for when the event bubbles up.

Carrying on from this, might it be wise to just bind every event we might ever want to the document itself? No. Don’t do that. Ever. And by “don’t do that”, I mean binding all the events. It actually makes sense in certain situations to do this. For instance, imagine you’re developing a large web application with predominantly dynamically inserted HTML, and in the HTML, you have a lot of <input /> elements that need validating. Instead of attaching an event listener to each INPUT that needs validation, we could put a data-validation="email", for example, attribute onto the element in HTML, then apply this listener on the document (using jQuery):

$(document).on('keypress', '[data-validation="email"]', function(e){
    console.log('Keypress detected inside the element');

There we go, so now we can handle all the validation needed for email input in one place, with one event listener, and all we need to do is use this attribute each time:

<input type="text" data-validation="email" />

Problem solved.

Jumping back a little, in the above situation it works nicely. However, I strongly recommend against binding every event listener to the document, it can lead to awful performance experiences. Instead, follow the general rule of thumb of apply the event listener to the parent element that is immediately above your dynamic elements.


You’ll notice above that to implement a generic solution for event delegation, we actually resorted to using jQuery. If you noticed in our Anchormen example, you’ll see we had to check the nodeName property to see if it was the element we were interested in. However, this solution wasn’t very re-usable, and would be tedious to have to use this IF check each time. Unlike our jQuery example, which was reasonably elegant. Native, elegant and re-usable event delegation in JavaScript is usually fairly tricky to implement, which is why, unless you only need it for a couple of event listeners, I’d recommend you use jQuery’s built-in support for event delegation, or hand off the responsibility to a dedicated event delegation library.

My usual rule of thumb is that if I’m needing to support IE8 (or even IE9), I’ll more than likely use jQuery, as it smoothes over a lot of the quirks you hit in legacy browsers. However, if I wasn’t needing to support those browsers, and I didn’t want to use jQuery, I may be tempted to use Gator.js. Which provides a simple interface for event delegation, like so:

var anchormen = document.querySelector('.anchormen');
Gator(anchormen).on('click', 'li', function(){

Gator.js is powered by the slightly experimental method matches on the Element prototype. You can read more on Element.matches on MDN. The basic principle is that you could see if a selector string would also match the element you’re calling matches on. Like so:

<h1 class="lookAtMe">Look at me!</h1>
var h1 = document.querySelector('h1');
console.log(h1.matches('.lookAtMe')); // true

You can imagine how being able to know if one element is matched by a selector might allow Gator.js to function properly, it will check whether the original element in the event matches the selector string you pass it as the second parameter to Gator’s on method.

Hopefully that’s served as a good primer on event delegation, and some of it’s practical use-cases. And hey, if you’re feeling brave enough, maybe give writing your own mini event delegation library a go!