Is there a way to fix event Listener from getting removed after deleting and restoring the same node? [duplicate]
Solution 1:
As of jQuery 1.7 you should use jQuery.fn.on
with the selector parameter filled:
$(staticAncestors).on(eventName, dynamicChild, function() {});
Explanation:
This is called event delegation and works as followed. The event is attached to a static parent (staticAncestors
) of the element that should be handled. This jQuery handler is triggered every time the event triggers on this element or one of the descendant elements. The handler then checks if the element that triggered the event matches your selector (dynamicChild
). When there is a match then your custom handler function is executed.
Prior to this, the recommended approach was to use live()
:
$(selector).live( eventName, function(){} );
However, live()
was deprecated in 1.7 in favour of on()
, and completely removed in 1.9. The live()
signature:
$(selector).live( eventName, function(){} );
... can be replaced with the following on()
signature:
$(document).on( eventName, selector, function(){} );
For example, if your page was dynamically creating elements with the class name dosomething
you would bind the event to a parent which already exists (this is the nub of the problem here, you need something that exists to bind to, don't bind to the dynamic content), this can be (and the easiest option) is document
. Though bear in mind document
may not be the most efficient option.
$(document).on('mouseover mouseout', '.dosomething', function(){
// what you want to happen when mouseover and mouseout
// occurs on elements that match '.dosomething'
});
Any parent that exists at the time the event is bound is fine. For example
$('.buttons').on('click', 'button', function(){
// do something here
});
would apply to
<div class="buttons">
<!-- <button>s that are generated dynamically and added here -->
</div>
Solution 2:
There is a good explanation in the documentation of jQuery.fn.on
.
In short:
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to
.on()
.
Thus in the following example #dataTable tbody tr
must exist before the code is generated.
$("#dataTable tbody tr").on("click", function(event){
console.log($(this).text());
});
If new HTML is being injected into the page, it is preferable to use delegated events to attach an event handler, as described next.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. For example, if the table exists, but the rows are added dynamically using code, the following will handle it:
$("#dataTable tbody").on("click", "tr", function(event){
console.log($(this).text());
});
In addition to their ability to handle events on descendant elements which are not yet created, another advantage of delegated events is their potential for much lower overhead when many elements must be monitored. On a data table with 1,000 rows in its tbody
, the first code example attaches a handler to 1,000 elements.
A delegated-events approach (the second code example) attaches an event handler to only one element, the tbody
, and the event only needs to bubble up one level (from the clicked tr
to tbody
).
Note: Delegated events do not work for SVG.
Solution 3:
This is a pure JavaScript solution without any libraries or plugins:
document.addEventListener('click', function (e) {
if (hasClass(e.target, 'bu')) {
// .bu clicked
// Do your thing
} else if (hasClass(e.target, 'test')) {
// .test clicked
// Do your other thing
}
}, false);
where hasClass
is
function hasClass(elem, className) {
return elem.className.split(' ').indexOf(className) > -1;
}
Live demo
Credit goes to Dave and Sime Vidas
Using more modern JS, hasClass
can be implemented as:
function hasClass(elem, className) {
return elem.classList.contains(className);
}
The same jsfiddle Live demo embeded below:
function hasClass(elem, className) {
return elem.classList.contains(className);
}
document.addEventListener('click', function(e) {
if (hasClass(e.target, 'bu')) {
alert('bu');
document.querySelector('.bu').innerHTML = '<div class="bu">Bu<div class="tu">Tu</div></div>';
} else if (hasClass(e.target, 'test')) {
alert('test');
} else if (hasClass(e.target, 'tu')) {
alert('tu');
}
}, false);
.test,
.bu,
.tu {
border: 1px solid gray;
padding: 10px;
margin: 10px;
}
<div class="test">Test
<div class="bu">Bu</div>test
</div>