Muhammad Umer
Muhammad Umer

Reputation: 18097

appending element and removing it destroys all event handlers in jquery?

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

Answers (4)

adeneo
adeneo

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.

FIDDLE

Upvotes: 2

Josh Crozier
Josh Crozier

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.

Example Here

$(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.

Example Here

div.find('button').detach();
div.append(btn);

Upvotes: 10

dfsq
dfsq

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

Vitaliy Terziev
Vitaliy Terziev

Reputation: 6671

put this after the second div.append(btn); - > btn = $('button').text('hi').click(function(){console.log(3);});

Upvotes: 2

Related Questions