Cloning Nodes
This time, we'll take a look at re-binding event handlers. But before we do, I should mention that, as of jQuery version 1.2, event handlers can be cloned along with elements. Consider this unordered list:
PLAIN TEXT
HTML:
<ul id="list3" class="eventlist"> <li>plain</li> <li class="special">special <button>I am special</button></li> <li>plain</li></ul>
We can make a copy of
<li class="special">
and insert it after the original, while at the same time retaining any event handlers that were attached within the original. The plain .clone()
method doesn't work that way; instead, it just copies the element:
PLAIN TEXT
JavaScript:
$(document).ready(function() { $('#list3 li.special button').click(function() { var $parent = $(this).parent();
$parent.clone().insertAfter($parent);
});
});
To get the event handlers copied over as well, all we have to do is pass in
true
to the method's single argument:JavaScript:
$(document).ready(function() { $('#list4 li.special button').click(function() { var $parent = $(this).parent();
$parent.clone(true).append(' I\'m a clone!').insertAfter($parent);
});
});
.clone(true)
is great when we want to make a copy of existing elements and their event handlers, but there are plenty of other situations that don't involve cloning in which we might want event handlers to persist.Re-binding Basics
The basic concept behind re-binding is fairly straightforward: We create a function that binds the handlers and then call it whenever new elements are introduced. For example, with our unordered list above, we first create a function calledaddItem
that registers the click handler, which in turn will add a new item:
PLAIN TEXT
JavaScript:
function addItem() { $('#list5 li.special button').click(function() { var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
});
}
Next, we call that function when the DOM initially loads:
PLAIN TEXT
JavaScript:
$(document).ready(function() { addItem();
});
Finally, we can call the function inside the click handler—and inside itself. That way, it will bind the event handlers to the new list item as well.
We'll add one more click handler to the button, but this one will not be re-bound, so that we can see the difference.
Here is what the code for buttons in #list5 looks like, all together:
PLAIN TEXT
JavaScript:
function addItem() { $('#list5 li.special button').click(function() { var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItem();
});
}
$(document).ready(function() { addItem();
// non-rebinding click handler ... $('#list5 li.special button').click(function() { $(this).after(' pressed');
});
});
However, what we've just done produces unwelcome results if we click on a button more than once. The click handler is bound again with each click of a button, producing a multiplier effect. The first click of a button creates one extra list item; the second creates two; the third, four; and so on.
Unbind, then Bind
To avoid the multiple binding, we can unbind first and then re-bind. So in line 2, instead of this ...$('#list5 li.special button').click(function() {
... we'll have this ...
$('#list6 li.special button').unbind('click').bind('click', function() {
Note the use of
.bind()
here. This is the universal event binder that jQuery uses. All the others, such as .click()
, .blur()
, .resize()
, and so on, are shorthand methods for their .bind('event')
equivalent. The complete new code, again with the additional non-rebinding click handler for contrast, looks like this:
PLAIN TEXT
JavaScript:
function addItemUnbind() { $('#list6 li.special button') .unbind('click') .bind('click', function() { var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItemUnbind();
});
}$(document).ready(function() { addItemUnbind();
// non-rebinding click handler $('#list6 li.special button').click(function() { $(this).after(' pressed');
});
});
See how this one works:
- plain
- special
- plain
addItemUnbind()
function went too far, unbinding the "non-rebinding" click handler as well, before it even had a chance to run once (it's evident because there is no "pressed" text after the "I am special" button here). Clearly, we're going to have to be more careful about what we're unbinding.
Event Namespacing
One way to avoid the over-reaching event unbinding is to apply a "namespace" to the click event for both binding and unbinding. So, instead of.bind('click')
and.unbind('click)
, we'll have, for example,.bind('click.addit')
and.unbind('click.addit)
. Here is one last code sample, which looks identical to the previous, except that it now has the namespaced event (and the list id is list7):
PLAIN TEXTJavaScript:function addItemNS() { $('#list7 li.special button') .unbind('click.addit') .bind('click.addit', function() { var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItemNS();
});
}$(document).ready(function() { addItemNS();
// non-rebinding click handler $('#list7 li.special button').click(function() { $(this).after(' pressed');
});
});
Bonus: Unbind by Function Reference
If you've made it this far, then you must have extraordinary patience, in which case I'll reward it with one final method of rebinding. Rather than namespace the events, we can reference the function in the second argument of the.bind()
and .unbind()
methods. We have to shuffle things around a bit to avoid "too much recursion," but it'll do just fine like so:
PLAIN TEXT
JavaScript:
function addItemFinal() { var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
$('#list8 li.special button') .unbind('click', addItemFinal) .bind('click', addItemFinal);
}
$(document).ready(function() {$('#list8 li.special button').bind('click', addItemFinal);
// non-rebinding click handler $('#list8 li.special button').click(function() { $(this).after(' pressed');
});
});
Plugin Options
There are three great plugins that can do a lot of this work for us:- Live Query by Brandon Aaron
- Listen by Ariel Flesler
- Intercept by Ariel Flesler
Update: Event Namespacing with Add and Remove
JavaScript:
function addRemoveItemNS() { var $newLi = $('<li class="special">special and new <button class="addone">I am new</button> <button class="removeme">remove me</button></li>');
$('#list9 li.special') .find('button.addone') .unbind('click.addit') .bind('click.addit', function() { $(this).parent().after($newLi);
addRemoveItemNS();
}) .end() .find('button.removeme') .unbind('click.removeit') .bind('click.removeit', function() { $(this).parent().remove();
});
}
$(document).ready(function() { addRemoveItemNS();
});
No comments:
Post a Comment
Please don't spam, spam comments is not allowed here.