Using Delegate and Undelegate in jQuery 1.4.2

read 24 comments

As some of you have heard, there have been two new methods added in jQuery 1.4.2, .delegate() and .undelegate(). These methods achieve the same thing as the .live() and .die() methods, they just use a different syntax. For those new to .live(), it's a method in jQuery that allows you to attach events to elements that appear in the document as well as elements that will appear in the future. An example would be if you attached a click event via .live():

JavaScript:
  1. $('img.photo').live('click', function(){
  2.   lightboxify(this);
  3. });

Then appended some photos via ajax later on:

JavaScript:
  1. // append an image
  2. $('body').append('<img src="face.jpg" alt="silly face" class="photo"/>');

The click event would still apply to that new image without having to re-bind the event. Handy, isn't it?

Continue Reading Below

Not too long ago, the .live() method was brought up for discussion for a few reasons. One problem discussed is that .live() fails when you try to use it alongside traversals like:

JavaScript:
  1. // FAILS
  2. $('ul').find('li').next().live('click', function(){});
  3. // FAILS
  4. $('ul').parent().nextAll().live('click', function(){});

and also when you pass any native DOM elements like:

JavaScript:
  1. // FAILS
  2. $(document.body).live('click', function(){});

Unfortunately, when you use .live(), it has to be at the top of the chain like so:

JavaScript:
  1. // WORKS
  2. $('ul li').live('click', function(){})

Because this can be frustrating and confusing for many users who are used to the traversing and chainability that jQuery offers, it sparked a discussion about the syntax for .live(). Why does it look like all the other methods, yet does not behave the same? Since changing the syntax would result in a whirlwind of code breakage, the jQuery team decided to introduce .delegate() and .undelegate() to complement .live() and .die(). Here's an example of how you would normally use .live() and .die() and how you can now use .delegate() and .undelegate():

Old way

JavaScript:
  1. // Using .live()
  2. $("table").each(function(){
  3.   $("td", this).live("hover", function(){
  4.     $(this).toggleClass("hover");
  5.   });
  6. });
  7.  
  8. // Using .die()
  9. $("table").each(function(){
  10.   $("td", this).die("hover");
  11. });

New way

JavaScript:
  1. // Using .delegate()
  2.  
  3. $("table").delegate("td", "hover", function(){
  4.   $(this).toggleClass("hover");
  5. });
  6.  
  7. // Using .undelegate()
  8. $("table").undelegate("td", "hover");

The benefit of delegate() is that it allows you to specify its context. This way, it ensures that we do not bubble all the way up the DOM tree to capture the target of the element. With the .live() method, it bubbles all the way up the DOM each time unless you set context like so: $('td', $('table')[0]).live('hover', function(){}). That just looks ugly.

Some often like to think of delegate() like a bind() call. The syntax is a little different as you can see below.

JavaScript:
  1. // .bind() way
  2. $('ul li').bind('click', function(e){
  3.   // Do something with bind
  4. });
  5.  
  6. // .delegate() way
  7. $('ul').delegate('li', 'click', function(e){
  8.   // Do something with delegate
  9. });

In short, the difference between .bind() and .delegate() is that .bind() will only add events to the elements that are on the page when you call it. .delegate() is listening for new elements and then adding events to them when they appear on the page.

The gotchas of delegate

While it does behave like .bind(), it does not allow you to pass an object map of events like .bind() does. Take this .bind() method for example:

JavaScript:
  1. // This works wonderfully
  2. $('ul li').bind({
  3.   click: function(e){
  4.     // Something on click
  5.   },
  6.   mouseover: function(e){
  7.     // Something on mouse over
  8.   }
  9. });

An error will be thrown when you try to do:

JavaScript:
  1. // FAILS!
  2. $('ul').delegate('li', {
  3.   click: function(e){
  4.     // Something on click
  5.   },
  6.   mouseover: function(e){
  7.     // Something on mouse over
  8.   }
  9. });

I'm not sure the reasoning behind not implementing this, but I guess I'm not the only one pondering it.

Granted, .bind() didn't have this feature until jQuery 1.4. But if you'd like this same feature in .live() and .delegate(), Robert Katic wrote a small piece of code that you can include. Grab the gist here.

I recommend using Robert Katic's patch above, but of course there are other approaches people can take. For example, you can rig up your own custom object map:

JavaScript:
  1. var customObjMap = {
  2.   click : function(e){
  3.     // Something on click
  4.   },
  5.   mouseover : function(e){
  6.     // Something on mouse over
  7.   }
  8. };
  9.  
  10. $('ol').delegate('li', 'click mouseover', function(e){
  11.   if($.isFunction(customObjMap[e.type])){
  12.     customObjMap[e.type].call(this, e);
  13.   }
  14. });

Another "gotcha" with both .delegate() and .live() is that when you add the events mouseenter and mouseleave to an element, and then check the event type (e.type) in the callback function, it incorrectly displays as mouseover and mouseout. Using .bind(), on the other hand, it displays as mouseenter and mouseleave as expected. Here is an example:

JavaScript:
  1. $('ol').delegate('li', 'mouseenter', function(e){
  2.   alert(e.type); // outputs mouseover
  3. });
  4.  
  5. $('ol li').bind('mouseenter', function(e){
  6.   alert(e.type); // outputs mouseenter
  7. });

UPDATE: This has been fixed and will be rolled out in the next version of jQuery.

Overall, the "gothcas" are no match for the benefits that .delegate() and .undelegate() provide. Truly great additions to the jQuery core.


comment feed

24 comments

  1. Fabrizio

    Hello,

    Thank you for this article ... and sorry for my English ... When we use $('img').live('click' ... for example, jquery tests if the click event is on an image. This is not really a good idea because all clicks events are tested. But it's very pratical. I would like to know if delegate() works in the same way ?

  2. @Fabrizio - Yes, delegate() will perform the same checks. The filtering checks that you mention CAN be a performance bottleneck, but it is not something I would be concerned about when dealing with relatively infrequent events, such as clicks. If you're dealing with something that could potentially occur very frequently (such as mouseenter), then you should focus on delegating as low in the DOM tree as possible.

    While live() allows you to specify a context as well (as of 1.4), delegate() happens to make setting the context very natural, as opposed to a somewhat awkward additional parameter in the live method. Whether you're using delegate or live, setting the lowest guaranteed context is always a good idea, as these checks will only occur on elements within them (rather than every parent on up to the document).

  3. Great article.

    Btw: Brandon Aaron has posted an article about the same topic, which additionally gives some good background info about this topic: http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery.

  4. The other big advantage is that $('a').live(..) actually goes and selects all the a elements only to throw them away.

    If you are using live then it is a good idea to put such code NOT in dom ready to speed up the page load.

    delegate method does not actually select elements. And that is a plus.

  5. Look at this gist to see how delegate breaks chainability.

    • I forked the gist you created to use an actual equivalent approach to the bind snippet in the gist you provided. ( Also replied to your comment on my blog post :)

      You had the following bind snippet:

      $('#container a').filter('.top').bind('click', function(e){
          log( $(this).text() + ' clicked1');
          return false;
      });
      

      This would be the equivalent delegate snippet:

      $('#container').delegate('a.top', 'click', function(e) {
          log( $(this).text() + ' clicked1');
          return false;
      });
      

      You want to only bind the event to the parent and filter on the "a.top". In your examples in the gist you were still binding the click event to each "a" tag itself instead of the parent... therefore losing the benefit of delegation.

      • Thanks Brandon. I had a typo in the fourth example. As you can see I commented above the fourth example that it works but forgot to put in working code. Have updated the gist to reflect that.

        However as you can see delegate helps a lot but still breaks the chainability.

        • brad

          What Brandon is saying is that you're missing the point of delegation. You never want to do $("#container a").delegate... delegation is attaching the events to the container, not the direct element. Also .delegate doesn't break chainability as your comment on the final example says, as it always returns the right jQuery object, the one referencing the container. So this works:

          $("#container")
          .delegate("p","click",function(){})
          .delegate("a","click",function(){});

  6. John Gadbois

    Sorry if this is a stupid question, but can't you just use .live() in conjunction with a more specific selector in most of these cases?

  7. @ John Gadbois: You can, but it's a little heavier (not as performant) as it has to bubble up the DOM tree each time a new element gets added to the page.

    
    // Using live, it will bubble all the way up the DOM tree to assign the event
    $('ul li').live('click'...
    // Using delegate, it will only bubble up to the ul element
    $('ul').delegate('li', 'click'...
    
    

    If you wanted to stick with live, you should be setting a DOM element as context for the second parameter like so:

    
    $('li', $('ul')[0]).live('click'...
    
    

    I'd suggest just to use delegate ;)

  8. Wow. As usual, a concept that was a little mysterious to me is now crystal clear because of this blog. Thank you!

  9. Robert

    Can someone give me an example where delegate would be necessary? I just can't seem to figure out how this would be useful. And I've never used live or die either so I don't fully understand why that would be handy either...

  10. @Robert: Let's say you bind a "click" event to an element when the document is ready

    
    $(document).ready(function(){ 
        $('div a').click(function(){
            alert('You clicked on an anchor element!');
        });
    });
    
    

    Then you append some anchor tags later on via ajax (after the document.ready function runs).

    
    $.get('someurl.php', function(data){
        // data coming back is: '<div><a href="#" rel="nofollow">Some link coming back via ajax!</a></div>'
        $('body').append(data);
    });
    
    

    If you try and click on that link, it will not alert anything. You have to re-bind the click event cause it wasn't on the page when document.ready was called. To avoid this, you can use the live/delegate method.

    
    $(document).ready(function(){
        $('div').delegate('a', 'click', function(){
            alert('You clicked on an anchor element!');
        })
    });
    
    

    Now if you append some code via ajax later on, it will have that 'click' event tied to it. No need to re-bind :)

  11. varmin

    Thank goodness I found this, I was going crazy trying to figure out why using 'hover' with .delegate() wasn't returning the type 'mouseenter'.

  12. A pretty good article, I must say, but I do have one notable issue with it.

    In short, the difference between .bind() and .delegate() is that .bind() will only add events to the elements that are on the page when you call it. .delegate() is listening for new elements and then adding events to them when they appear on the page.

    That statement is immensely inaccurate. Delegate doesn't do any sort of polling for DOM changes or listening to any sort of events fired from DOM alterations.

    In essence, it 'feels' like it's doing that, but it's not really adding in new events at all to the sub-elements. It adds one handler only, to one element, which handles the events that bubble up from its children that match the selection.

    I realize that it sounds like a nitpick, but it's really of note. There are no extra event handlers added outside of just the one, which can result in improved performance when contrasted with adding an event handler to dozens (or more) elements, while also gaining the live nature where new elements that match seemingly inherit the event.

  13. Thanks, this is useful!

  14. Ryan Blunden

    Thanks so much for this clean and easy to understand explanation of what delegate does and what the benefits are over live.

  15. Edwin

    hey thanks for this article.. i understand more about the difference..

    one question. If you bind live directly to an id is it then better than delegate?

    $('#idname').live(click(function){

    })

    If you use live like this does it bubble also?

  16. siva

    Hi im new to jquery can you suggest tutotrials or ebooks or good sites that are practical at the same time easy to undestand your reply is really helpful

  17. Help

    please me for autocomplete using this version of jquery with webservice and returning a value in hidden field.

    thank you.

    sorry for my english

  18. Thank you for the post. I'm new with delegate and this helped much. Btw, the multiple delegate works now, see:

    
    // WORKS NOW withoud plugin :-)
    $('ul').delegate('li', {
      click: function(e){
        // Something on click
      },
      mouseover: function(e){
        // Something on mouse over
      }
    });

    You may update this.

    Kind regards
    Patrick

  19. So in a sense, .delegate() can listen for new elements to be added to the Dom. What is the performance hit for always listening, if if there is even a hit at all?

7 Pings

  1. Linkhub – Woche 09-2010 « pehbehbeh

    [...] man delegate und undelegate in jQuery 1.4.2 [...]

  2. [...] method, we can use that one instead. Jordan Boesch recently wrote a nice introductory article on Using Delegate and Undelegate in jQuery, so check that out if you’re uncertain about what the method does. One thing I’d like [...]

  3. 8 jQuery Performance & Optimization Tips You Need In 2010 | AddyOsmani.com | Where Web Businesses Grow

    [...] core features: http://www.learningjquery.com/2010/03/using-delegate-and-undelegate-in-jquery-1-4-2 and  [...]

  4. 8 jQuery Performance & Optimization Tips You Need In 2010 | DesignerLinks | Home to Web design news, jQuery Tutorials, CSS tutorials, Web Designing tutorials, JavaScript tutorials and more!

    [...] core features: http://www.learningjquery.com/2010/03/using-delegate-and-undelegate-in-jquery-1-4-2 and  [...]

  5. [...] method, we can use that one instead. Jordan Boesch recently wrote a nice introductory article on Using Delegate and Undelegate in jQuery, so check that out if you’re uncertain about what the method does. One thing I’d like [...]

Sorry, but comments for this entry are now closed.