Reputation: 18097
Ok I create element, assign click handler, and append it to body. Then i remove it and reappend it and click handler is no longer working???
Why would this happen.
var btn = $('<button>').text('hi').click(function(){console.log(3);});
var div = $('<div>');
div.append(btn);
$('body').append(div);
//click it now, it works..
div.html('');
div.append(btn);
// now button doesn't work..
So why is this happening and what can i do to fix it.
Upvotes: 8
Views: 5363
Reputation: 318182
It happens because you're calling html()
on the DIV that contains the button.
When you call html()
with an empty string, it calls empty()
internally.
Calling empty()
on an element iterates over all the elements inside that element removing all data and events securely.
It does this by calling jQuery.cleanData
on the button, which again explicitly calls jQuery.removeEvent
, removing all events on the button.
The button is still stored in the variable btn
, so it can be appended again, but it has lost all data and any events attached to it because the parent element had html("")
called on it.
The solution is to use detach()
to remove the element with all the data and events intact, so it can be appended again, or you can attach the event to a parent element that isn't removed, or you could just hide the element, generally there's no reason to remove the element just to reappend it, it's better to hide it.
Upvotes: 2
Reputation: 240878
Since .html('')
is essentially the same as .empty()
, the following applies (from the jQuery docs):
To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.
If you want to remove elements without destroying their data or event handlers (so they can be re-added later), use
.detach()
instead.
One option would be to use event delegation. In doing so, the event isn't bound directly to the button
element, it is bound to a constant parent element that isn't removed.
$(document).on('click', 'button', function () {
// ..
});
As mentioned above, another option would be to use the .detach()
method in order to remove the element from the DOM, without removing attached event listeners.
The
.detach()
method is the same as.remove()
, except that.detach()
keeps all jQuery data associated with the removed elements. This method is useful when removed elements are to be reinserted into the DOM at a later time.
div.find('button').detach();
div.append(btn);
Upvotes: 10
Reputation: 193261
This is very interesting situation. What happens when you clear div
with html('')
method. Take a look at source code and you will see that internally jQuery calls jQuery.cleanData(getAll(elem, false));
. This method is responsible for removing all the related data
for all child elements that have already been removed. This is important in order to avoid memory leaks.
Clearing data also removes events bound with on
(and similar) methods, because those event handlers are also stored in internal cache object.
So as the result, even though you removed content of the div
, the btn
object is still in memory, but the event bound to it previously is gone.
This was the explanation of the problem. The solution is to use dedicated method called detach
. It will remove button from the DOM but will keep event data in case element will later be appended again.
// remove element but keep its data
btn.detach();
// append back
div.append(btn);
In situations like this you should not use html('')
.
Upvotes: 2
Reputation: 6671
put this after the second div.append(btn); - > btn = $('button').text('hi').click(function(){console.log(3);});
Upvotes: 2