Working with Events, part 2
In my last article, I described the common problem of events seemingly ceasing to work for new elements added to a document, whether by some form of ajax or by DOM modification. We examined one way to overcome the problem: Event Delegation. With event delegation, we bind the event handler to a containing element that remains in the DOM and then check for the target of the event.
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:
-
<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:
-
});
-
});
Try it:
- plain
- special
- plain
As you can see, the original button is able to keep creating new list items, but the buttons inside the "dynamically generated" list items can't create new ones.
To get the event handlers copied over as well, all we have to do is pass in true to the method's single argument:
Note: I've added .append(' I\'m a clone!') here only as visual reinforcement of what is going on.
Try it now:
- plain
- special
- plain
Using .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 called addItem that registers the click handler, which in turn will add a new item:
Next, we call that function when the DOM initially loads:
-
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:
-
function addItem() {
-
var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
-
addItem();
-
});
-
}
-
-
addItem();
-
-
// non-rebinding click handler ...
-
});
-
});
Try this one out:
- plain
- special
- plain
We can see that "pressed" is appended to the first list item each time it is clicked, but it is not appended to any of the list items created by our clicks. On the other hand, the created buttons are able to generate further list items because that function is being rebound.
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:
-
function addItemUnbind() {
-
$('#list6 li.special button')
-
var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
-
addItemUnbind();
-
});
-
}
-
addItemUnbind();
-
-
// non-rebinding click handler
-
});
-
});
See how this one works:
- plain
- special
- plain
Unfortunately, our attempt to unbind the 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):
-
function addItemNS() {
-
$('#list7 li.special button')
-
var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
-
addItemNS();
-
});
-
}
-
addItemNS();
-
-
// non-rebinding click handler
-
});
-
});
We should now — finally! — have a set of behaviors attached to these buttons that act the way we intend them to:
- plain
- special
- plain
For more information about event namespacing, read Brandon Aaron's article, Namespace Your Events.
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:
-
function addItemFinal() {
-
var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
-
$('#list8 li.special button')
-
}
-
-
-
// non-rebinding click handler
-
});
-
});
Note here that there are no parentheses after "addItemFinal" when it appears inside the bind/unbind, because we are referencing the function, not calling it. So let's test it out one last time:
- plain
- special
- plain
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
If this entry left you bewildered, or if you want a quick, well tested solution, you should definitely try one of them. Each works a little differently, but they're all super plugins.



May 19th, 2008 at 9:56 am
hi,
you really put something into this article. thanks.
to avoid the double-binding of events, why dont we just "tag" the elements that already got the event attached
maybe faster than unbinding/rebinding *all* events.
of course we still have to call addEvent() when needed:-/...
-till
May 19th, 2008 at 10:26 am
Hi Till,
Yes, tagging elements that have the event already bound is a great way to avoid the multiple binding problem. Thanks for the suggestion!
May 20th, 2008 at 11:15 am
Learning jQuery » Working with Events, part 2...
Learning jQuery » Working with Events, part 2...
June 12th, 2008 at 4:50 pm
First off, I'm starting to seriously look at jQuery as a tool for everyday scripting needs, and this site is a great way of showcasing the value of this library. Thank you!
Second off, I'm writing because I primarily use Safari (3.1.1) for the Mac (OS X 10.5.3) and the .clone(true) example didn't work. The cloning part worked, but the event binding did not. I tested this in Firefox also, and everything worked fine, so I know it's not a problem with your code but, rather, with Safari.
June 12th, 2008 at 9:24 pm
$(document).ready(function() {
$('#list4 li.special button').click(function() {
var $parent = $(this).parent();
$parent.clone().append(' I\'m a clone!').insertAfter($parent);
});
});
June 16th, 2008 at 9:08 am
My AJAX call is updating DOM of the upload form by creating one additional input box which I need to submit along with values of other controls existing in the DOM prior to AJAX call.
However, this is not the case. Although I can see the value of the new element, it does not get posted. The workaround I am using is to copy the value of this element on submit to another, hidden, existing prior to AJAX call, and retrieve that value after submitting. Is there a way that I make the form aware that it should post the value of the new element created by AJAX?
July 17th, 2008 at 1:42 am
Great article, thanks!
August 18th, 2008 at 8:50 pm
What is my problem
I´m using this code inside an javascript function
and i need to press the button where i caal this function twice to make my php the code run
function GetData(_url, n){
$(document).ready(function(){
$("#save").click(function(){
$("#res p").load("salvar_lista.php"+_url+"&lista=lista"+n);
});
});
}
August 20th, 2008 at 3:13 am
Hello Karl.
I have one question about this bind & unbind.
If we take a line of your code as an example.
Here after insertion I don't want to perform the old functionality. Lets say on this button's click Event i want an alert. How is that possible.....
Please Explain.....soon
-----------------------------
I changed the id of button and onClick call an alert("ABC"); but invan. (Even remove all unnecessary ids etc..)
August 20th, 2008 at 8:27 am
Hi Tahir,
You simply bind the click event to the new
<li>after insertion. An easy way to do this is to start with$newLiand use.insertAfter()rather than.after(). Here is a simplified example:That should allow the newly created button to produce an alert when clicked.
August 23rd, 2008 at 4:57 am
Thanks Karl.
You really gave me great reply.
Ok. I have a question in jquery documentation it is said that .bind will bind every matched element on the document. but with .bind this statement does not feel true. I copy and paste list <ul> and it work on first one fine but on second list it does not respond. Even i copy the code from Jquery.com and learningjquery.com and apply the same practice. is there any problem or modification of code required is it possible without changing the #id of 2nd <ul> and then bind is again.
August 23rd, 2008 at 8:46 am
Hi Tahir,
I'm not sure I follow your question. Are you using the same ID for more than one <ul>? If so, that will cause problems. If you could post a test page that exhibits the problem you're having, I'll have a better chance of detecting the problem
August 23rd, 2008 at 2:41 pm
I hope the question is relative: Suppose I have a page that is loaded once and anything on it gets done through AJAX. I have a "main" div which is updated through an ajax call when a click is made.
For example, at the beginning main contains some anchors with class="alert" which bind an alert to click(). At some point in time, main is replaced by some new HTML (from an AJAX call) so there are no more anchors with class="alert".
The question is: Are the initially bound handlers also cleared along with the removed elements? Do I have to unbind everything before an AJAX call replaces html with bound handlers to avoid memory leaks?