Event delegation with JavaScript
There has been a fair share about JavaScript and event delegation, but since a lot of people doesn’t seem to have read it, I thought I’d re-iterate the point here. The more the merrier, right? 馃檪
HTML used for this post
The HTML used for this post, which can also be seen live in the event delegation demo page, looks like this:
<ul id="navigation">
<li><a href="http://www.robertnyman.com/code-libraries/">Code libraries</a></li>
<li><a href="http://www.flickr.com/photos/robertnyman">Flickr photos</a></li>
<li><a href="http://www.robertnyman.com/web-sites/">Web sites</a></li>
<li><a href="http://www.robertnyman.com/archives/">Archives</a></li>
<li><a href="http://www.robertnyman.com/geekmeet/">Geek Meet</a></li>
</ul>
Traditional event handling
As we’re all taught and how we start out is adding an event to each element we want the end user to be able to interact with; naturally, in an unobtrusive manner (basically, meaning that no events are inline in the HTML code, but rather applied directly from an external JavaScript).
window.onload = function () {
var navigation = document.getElementById("navigation");
var navigationLinks = navigation.getElementsByTagName("a");
for (var i=0; i<navigationLinks.length; i++) {
navigationLinks[i].onclick = function () {
/*
Something extraordinary that
only you thought of...
*/
}
}
};
Why that isn’t always so good
The problem with the above approach is that with a web page with a decent level of interaction, it will soon be full of events that the web browser have to keep track of, and it might also pose memory issues in some web browsers (yeah, just guess…).
When I met Peter-Paul Koch (ppk) at @media Ajax, one of the questions I asked him is how/if he deals with memory leakage in Internet Explorer. If I remember correctly, he said:
I don’t really do much to code around it. Just as long as you’re using event delegation, it’s practically not a problem.
Presenting event delegation
The general idea with event delegation is to have just one (or a few) events on elements far higher up in the HTML hierarchy, and from there, trace on what target element the event actually occurred, and then take appropriate action. The code from above reworked:
window.onload = function () {
var navigation = document.getElementById("navigation");
navigation.onclick = function () {
// Ta daa!
}
};
Looks a little bit simpler, right? And it definitely helps to avoid scattering your events all over the place.
But.. Hang on!
“Hey, wait a minute!” I hear you say! “Sure, you only applied one event, but you broke the functionality!”, Well, no. The key to succeeding with event delegation is in the function/method receiving the actual event, and from there, decide what to do. Let us take the code above a bit further:
window.onload = function () {
var navigation = document.getElementById("navigation");
navigation.onclick = function (evt) {
// Event tweaks, since IE wants to go its own way...
var event = evt || window.event;
var target = event.target || event.srcElement;
var href = "Link's href: " + target.getAttribute("href") + "\n\n";
// Completely simplifying below
var text = "Link's text: " + target.innerHTML;
alert(href + text);
}
};
As you might see, the possibilities and advantages of having centralized event handling are probably becoming increasingly clearer to you just as you read this. Your code will be leaner and have better performance, without sacrificing any of the functionality. Ain’t that great? 馃檪
JavaScript library support
As you might have guessed, most JavaScript libraries cater to this in one way or the other. With DOMAssistant, we simply provide a cross-browser way to determine on what actual element the event took place. Same code, but with DOMAssistant:
DOMAssistant.DOMReady(function () {
$("#navigation").addEvent("click", function (evt) {
var target = evt.eventTarget;
// And so on...
});
});
By the way, if anyone has a suggestion how we can make your event delegation life even easier with DOMAssistant, just let me know!
Event delegation with other JavaScript libraries
Looking around at what other JavaScript libraries offer, I found some quick links to help you out:
Event delegation demos
I’ve put together a very simple demo page for you to see event delegation in action; there’s also an event delegation demo using DOMAssistant.
Now, go happy event delegating! 馃檪
[…] to effectively use JavaScript Event Delegation to reduce code and increase […]
??? ??…
?? ???? ?? ?? event delegation….
It's not quite that simple — you need to make sure that the event target is the element you want to take action on. In your demo, click just to the left or right of the link text to see the problem.
It's easy to workaround by either walking up the DOM tree using a while loop and parentNode (while being careful to go further then you need of course), on in this particular case, since you are only interested in actions on the anchor tag, simply add a conditional to check evt.eventTarget's node name (assuming DOMAssistant resolves events from text elements, which it appears it does do).
This is a good method, but it doesn't work for all event types, only those that bubble.
Only this weekend I was writing what I expected to be a simple script for a page with 60(!) input fields where I needed things to happen each time one of those fields lost keyboard focus. I tried to use an event listener for the blur event on the parent, but it didn't work. It took me a while to realise that blur events don't bubble.
[…] Event delegation with JavaScript – Robert芒鈧劉s talk – Web development and Internet trends Event delegation with JavaScript (tags: development event javascript) […]
I'd heard of event delegation but never quite grasped it, but I realise now how simple and useful it is. Cheers Robert!
dAN
@Tommy Olsson
You can still use event delegation with focus and blur by making use of the capture phase (pass true as third param to addEventListener). Of course IE doesn't support this but is kind enough to provide focusin and focusout events which you can use to achieve the same effect.
@Curtis: I thought about using capture, but I needed the script to work in IE. Didn't know about focusin and focusout. It wasn't a big deal to assign the event listeners to the text fields instead, anyway. (It's a document that will only be used for a short period of time.)
BTW, congratulations to Robert for making #19 on Computer Sweden's list of Sweden's 75 top developers! 馃檪
Curtis,
Absolutely, with more advanced HTML or elements with margin etc, you do need to do some DOM traversing. DOMAssistant does exclude text nodes automatically, but to make it more efficient for advanced scenarios, I guess doing a sort of selector on the target and possible parent elements might be a good solution.
Tommy,
Peter-Paul Koch has written an article about event delegation with the focus and blur events, which is based on the techniques described by Curtis above. However, it would be nice to have event bubbling of all events, I think. Especially a lot of form scripting could be vastly improved.
And thanks for the congratulation, I'll cover that in my next post. And congratulations to you for spot #46. 馃檪
dAN,
Glad you liked it!
Another benefit of event delegation is that events are applied to elements created _after_ the events were applied (like ajax-generated content) as well as the elements that were there from the start.
Is there no chance of things ever getting slower because of ED? If you put every onclick-event on the body instead of the actual elements that users should click, won't it take a sec or so to sort out which callback to run whereas if the event was set on the actual element being clicked it would simply run it straight away?
I'm curious because I'm using my ImgZoom plug-in (which uses ED) on a site that already has loads of other ED (up to the same (body) elment) and when I click an image now it takes a sec or so before it shows up whereas on my demo-page you hardly got time to blink.
Very nice summary Robert!
One of the very nice aspects of using event delegation, which is somewhat understated in the post, is that your code also becomes a lot easier to maintain… even more so, when compared to inline event handlers.
It does take a little while to wrap your head around, but once you've got a firm grasp of the technique, the clarity of your code will improve quite a lot, and updating it will become easier.
Oh, and not to forget … using event delegates, makes for happy Ajax development, where you can assign a delegate on a parent element, and just replace it's content with Ajax, and all you spiffy event based javascripty goodness just works 馃檪
Chris Heilmann created a small experiment a couple of years ago, that has inspired me to change how I think about events, when doing scripting for browsers.
http://icant.co.uk/sandbox/eventdelegation/
Andreas,
I think it's important to apply the events at the best place in the HTML hierarchy; this is, of course, heavily context-dependent. The <code>body</code> might be suitable in some cases, but generally I think it's overkill. Generally, I think it's ok, for instance, to have one event for the navigation, one for the content etc.
It's a delicate balance which can result in slower times, if it's applied too high up in a page with a lot of elements. Basically, it's about being pragmatic. 馃檪
Morgan,
Good to see that we're on the same page! I actually linked to Chris' example above, where he does event delegation with YUI.
Robert,
nice introduction for your event delegation in DOMAssistant. When ?
You may see my NWEvents at work in an event delegation test here:
http://javascript.nwbox.com/NWEvents/delegates.html
Sorry for the fuzziness of the sample, it was just a combined test for all the quirks I found during development and to stress my NWMatcher selector engine mixed in with event delegation.
The best thing is that I even don’t need an “onload” or an “onReady” event to attach functionality to the page, this avoids the problem of “early page enlivenment” you talked about in another post.
Well since you already have a nice Event Manager it shouldn’t be too difficult to implement the “Light” part of event delegation, it will prove extremely useful in a project like DOMAssistant. And since I see you have built a very fast selector engine too it will combine perfectly.
Let me know, I will be happy to give info on my code. You can grab the latest versions from Google Code SVN.
Diego Perini
Diego,
Sounds great! I think it is a good idea to collaborate about this and together reach the best solution! I'll talk with the team about this.
[…] Event delegation with JavaScript […]
[…] If you aren’t familiar with javascript event delegation, this probably won’t mean a lot to you (here’s a quick tutorial).I went poking around in the jQuery revisions because I haven’t found hardly any details on what […]
[…] t贸pico recente, 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 […]
[…] Event Delegation with javascript […]
[…] Live events is a new feature in jQuery 1.3. It could be used as an alternative to traditional聽event delegation techniques.聽It provides a convenient way to implement event delegation.聽The following code will bind an […]
This article was very helpful, I needed to use event delegation in a project of mine. This article and another helped me with implementing what i needed. I also suggest: http://usingjquery.wordpress.com/2010/07/14/event-delegation-in-javascript-jquery/
john Mj,
Thanks, and thanks for the tip!
[…] Event Delegation with JavaScript – Robert Nyman […]
[…] If you want to read more information about Event delegation, you can take a look at this article. […]
[…] talk Event Delegation With JavaScript This entry was posted in Javascript and tagged Delegation, Event by eongoo. Bookmark the […]
[…] http://robertnyman.com/2008/05/04/event-delegation-with-javascript/ […]