|
| 1 | +--- |
| 2 | +title: Using Delegate and Undelegate in jQuery |
| 3 | +attribution: Jordan Boesch |
| 4 | +status: needswork |
| 5 | +editrequired: 2 |
| 6 | +source: http://www.learningjquery.com/2010/03/using-delegate-and-undelegate-in-jquery-1-4-2 |
| 7 | +--- |
| 8 | + |
| 9 | +As some of you have heard, there have been two new methods added in jQuery 1.4.2, [.delegate()](http://api.jquery.com/delegate/) and [.undelegate()](http://api.jquery.com/undelegate/). These methods achieve the same thing as the [.live()](http://api.jquery.com/live/) and [.die()](http://api.jquery.com/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()*: |
| 10 | + |
| 11 | +<div class="example" markdown="1"> |
| 12 | + $('img.photo').live('click', function(){ |
| 13 | + lightboxify(this); |
| 14 | + }); |
| 15 | +</div> |
| 16 | + |
| 17 | +Then appended some photos via ajax later on: |
| 18 | + |
| 19 | +<div class="example" markdown="1"> |
| 20 | + // append an image |
| 21 | + $('body').append('<img src="face.jpg" alt="silly face" class="photo"/>'); |
| 22 | +</div> |
| 23 | + |
| 24 | +The click event would still apply to that new image without having to re-bind the event. Handy, isn't it? |
| 25 | + |
| 26 | +Not too long ago, the *.live()* method was brought up for discussion for a [few](http://forum.jquery.com/topic/jquery-live-jquery-fn-live-discussion) [reasons](http://paulirish.com/2010/on-jquery-live/). One problem discussed is that *.live()* fails when you try to use it alongside traversals like: |
| 27 | + |
| 28 | +<div class="example" markdown="1"> |
| 29 | + // FAILS |
| 30 | + $('ul').find('li').next().live('click', function(){}); |
| 31 | + // FAILS |
| 32 | + $('ul').parent().nextAll().live('click', function(){}); |
| 33 | +</div> |
| 34 | + |
| 35 | +and also when you pass any native DOM elements like: |
| 36 | + |
| 37 | +<div class="example" markdown="1"> |
| 38 | + // FAILS |
| 39 | + $(document.body).live('click', function(){}); |
| 40 | +</div> |
| 41 | + |
| 42 | +Unfortunately, when you use *.live()*, it has to be at the top of the chain like so: |
| 43 | + |
| 44 | +<div class="example" markdown="1"> |
| 45 | + $('ul li').live('click', function(){}) |
| 46 | +</div> |
| 47 | + |
| 48 | +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()*: |
| 49 | + |
| 50 | +Old way |
| 51 | + |
| 52 | +<div class="example" markdown="1"> |
| 53 | + // Using .live() |
| 54 | + $("table").each(function(){ |
| 55 | + $("td", this).live("hover", function(){ |
| 56 | + $(this).toggleClass("hover"); |
| 57 | + }); |
| 58 | + }); |
| 59 | + |
| 60 | + // Using .die() |
| 61 | + $("table").each(function(){ |
| 62 | + $("td", this).die("hover"); |
| 63 | + }); |
| 64 | +</div> |
| 65 | + |
| 66 | +New way |
| 67 | + |
| 68 | +<div class="example" markdown="1"> |
| 69 | + // Using .delegate() |
| 70 | + $("table").delegate("td", "hover", function(){ |
| 71 | + $(this).toggleClass("hover"); |
| 72 | + }); |
| 73 | + |
| 74 | + // Using .undelegate() |
| 75 | + $("table").undelegate("td", "hover"); |
| 76 | +</div> |
| 77 | + |
| 78 | +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. |
| 79 | + |
| 80 | +Some often like to think of *delegate()* like a *bind()* call. The syntax is a little different as you can see below. |
| 81 | + |
| 82 | +<div class="example" markdown="1"> |
| 83 | + // .bind() way |
| 84 | + $('ul li').bind('click', function(e){ |
| 85 | + // Do something with bind |
| 86 | + }); |
| 87 | + |
| 88 | + // .delegate() way |
| 89 | + $('ul').delegate('li', 'click', function(e){ |
| 90 | + // Do something with delegate |
| 91 | + }); |
| 92 | +</div> |
| 93 | + |
| 94 | + |
| 95 | +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. |
| 96 | +The gotchas of delegate |
| 97 | + |
| 98 | +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: |
| 99 | + |
| 100 | +<div class="example" markdown="1"> |
| 101 | + // This works wonderfully |
| 102 | + $('ul li').bind({ |
| 103 | + click: function(e){ |
| 104 | + // Something on click |
| 105 | + }, |
| 106 | + mouseover: function(e){ |
| 107 | + // Something on mouse over |
| 108 | + } |
| 109 | + }); |
| 110 | +</div> |
| 111 | + |
| 112 | +An error will be thrown when you try to do: |
| 113 | + |
| 114 | +<div class="example" markdown="1"> |
| 115 | + // FAILS! |
| 116 | + $('ul').delegate('li', { |
| 117 | + click: function(e){ |
| 118 | + // Something on click |
| 119 | + }, |
| 120 | + mouseover: function(e){ |
| 121 | + // Something on mouse over |
| 122 | + } |
| 123 | + }); |
| 124 | +</div> |
| 125 | + |
| 126 | +I'm not sure the reasoning behind not implementing this, but I guess I'm not the only one pondering it. |
| 127 | + |
| 128 | +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](http://gist.github.com/310747). |
| 129 | + |
| 130 | +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: |
| 131 | + |
| 132 | +<div class="example" markdown="1"> |
| 133 | + var customObjMap = { |
| 134 | + click : function(e){ |
| 135 | + // Something on click |
| 136 | + }, |
| 137 | + mouseover : function(e){ |
| 138 | + // Something on mouse over |
| 139 | + } |
| 140 | + }; |
| 141 | + |
| 142 | + $('ol').delegate('li', 'click mouseover', function(e){ |
| 143 | + if($.isFunction(customObjMap[e.type])){ |
| 144 | + customObjMap[e.type].call(this, e); |
| 145 | + } |
| 146 | + }); |
| 147 | +</div> |
| 148 | + |
| 149 | +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: |
| 150 | + |
| 151 | +<div class="example" markdown="1"> |
| 152 | + $('ol').delegate('li', 'mouseenter', function(e){ |
| 153 | + alert(e.type); // outputs mouseover |
| 154 | + }); |
| 155 | + |
| 156 | + $('ol li').bind('mouseenter', function(e){ |
| 157 | + alert(e.type); // outputs mouseenter |
| 158 | + }); |
| 159 | +</div> |
| 160 | + |
| 161 | + |
| 162 | +Overall, the "gothcas" are no match for the benefits that *.delegate()* and *.undelegate()* provide. Truly great additions to the jQuery core. |
0 commit comments