11

Reading another Stack Overflow question about jQuery performance, I started thinking about the point when it becomes worth using event delegation rather than binding to elements individually. I was thinking mainly about jQuery, but I suppose it is probably applicable for Javascript in general.

Event delegation has two main purposes:

  1. allowing handlers to work on elements that have not yet been created/inserted into the DOM.
  2. binding one function to a common ancestor element rather than to multiple sibling elements

My question is about the second of these. The general answer will probably be "it depends on the exact situation", but I wonder if there is a rule of thumb or a way of benchmarking in order to test this.

So, the question is: how many elements do you need before the performance benefits of event delegation outweigh the performance costs?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • I don't know about the performance costs of event delegation... What are they? Events bubble up anyway, and the event target is is stored in the corresponding property of the event object at all times. – Šime Vidas Oct 15 '10 at 22:41
  • 1
    @Sime The main one I'd thought of was that every time an event is fired, the selector in the delegate call has to be run to check if it matches the event target. There may be others... – lonesomeday Oct 15 '10 at 22:44
  • 2
    Consider that when you use delegation, you're effectively spreading out the cost of binding handlers over the page lifetime. When you bind by finding elements first, you add more work at page load time, when users are most sensitive to delays. – Pointy Oct 15 '10 at 22:52
  • Can you post code? I think you have it backwards. – jcolebrand Oct 15 '10 at 22:53
  • Also it's not just about how many elements are involved - it's also how the events will be generated. For events that cause the page to be reloaded, it probably doesn't matter. For a game, where an event represents input from a real-time controller, it might make a difference. – Pointy Oct 15 '10 at 22:54
  • @lonesomeday - it's not exactly like that, bubbling carried out without testing objects against selectors. only `live` handlers are different – vittore Oct 15 '10 at 22:56
  • @lonesomeday The delegate() method of jQuery is one thing, and event delegation as I know it is another thing. From what I understand, event delegation is when you attach one event handler to a common ancestor element instead of attaching a handler for each element (like, attaching it to a table instead of each table cell). I don't think that this process has performance costs. The jQuery delegate() method, however, may have... – Šime Vidas Oct 15 '10 at 22:59
  • 1
    @vittore `$('#el').delegate('a[href^=http://www.google.com]', 'click', func)` The selector `a[href^=http://www.google.com]` will be run against the target of every click event that reaches `#el`, I believe. – lonesomeday Oct 15 '10 at 22:59
  • @Sime Maybe this question is too general and non-specific... I would have thought that you would almost *always* need to check the event target was the one you wanted when using delegation. – lonesomeday Oct 15 '10 at 23:02
  • @lonesomeday You don't with jQuery's delegation methods, because the selector you provide already achieves that. So jQuery itself checks but you don't have to in your own code. It's expensive, but remember checking a single element against a selector is much much cheaper than applying the selector to an entire page. – Pointy Oct 15 '10 at 23:09
  • @lonesomeday sorry misread you post, you are right, it will – vittore Oct 15 '10 at 23:10
  • It's not just a question of how many elements, but of how hard they are to select. If they all have id's, you can bind to more of them individually for the same cost as binding to significantly fewer elements which you can only select, for example, in terms of nesting ("ul ul li") or of an attribute's value. – Sid_M Oct 15 '10 at 23:25

1 Answers1

3

Assuming this HTML structure:

<ul id="navUL">
    <li><a href="one.html">One</a></li>
    <li><a href="two.html">Two</a></li>
    <li><a href="three.html">Three</a></li>
</ul>

Just to clear things up (for me) .... According to jQuery docs ( http://api.jquery.com/delegate/ ), this:

$("#navUL").delegate("a", "click", function(){
    // anchor clicked
});  

... is equivalent to this:

$("#navUL").each(function(){
    $("a", this).live("click", function(){
        // anchor clicked
    });
}); 

However, event delegation (as I know it) is this:

$("#navUL").click(function(e) {
    if (e.target.nodeName !== "A") { return false; }
    // anchor clicked
    // anchor is referenced by e.target
});  

So you catch the click event on the UL element, and then figure out which anchor was actually clicked via the event.target property.

I don't know about the delegate() method, but this last technique should always be faster than attaching event handlers to each anchor in the #navUL element, like so:

$("#navUL a").click(function() {
    // anchor clicked
    // anchor is referenced by the this value
});
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • Don't you need an `e.stopPropagation();` in your delegation as you know it example? – Peter Ajtai Oct 15 '10 at 23:26
  • Hm, why? The user clicks somewhere inside the #navUL element. The click event propagates to the #navUL element (since there are no click handlers attached to A or LI elements). Inside the #navUL click handler, I check if an anchor was clicked. If not, I just kill the event (via return false;). If yes, I do what I intended (with the anchor). I may want to prevent the default behavior of the anchor, but I don't see why I would want to prevent the click event from bubbling up the DOM tree (I don't have any click handlers set on ancestor elements, anyway). – Šime Vidas Oct 15 '10 at 23:34
  • OK, I can go with the "always" answer. It fits with what I do anyway. Thanks. – lonesomeday Oct 16 '10 at 11:47