jQuery - Trigger event when an element is removed from the DOM
JqueryEventsDomTriggersJquery Problem Overview
I'm trying to figure out how to execute some js code when an element is removed from the page:
jQuery('#some-element').remove(); // remove some element from the page
/* need to figure out how to independently detect the above happened */
is there an event tailored for that, something like:
jQuery('#some-element').onremoval( function() {
// do post-mortem stuff here
});
Jquery Solutions
Solution 1 - Jquery
You can use jQuery special events for this.
In all simplicity,
Setup:
(function($){
$.event.special.destroyed = {
remove: function(o) {
if (o.handler) {
o.handler()
}
}
}
})(jQuery)
Usage:
$('.thing').bind('destroyed', function() {
// do stuff
})
Addendum to answer Pierre and DesignerGuy's comments:
To not have the callback fire when calling $('.thing').off('destroyed')
, change the if condition to: if (o.handler && o.type !== 'destroyed') { ... }
Solution 2 - Jquery
Just checked, it is already built-in in current version of JQuery:
jQuery - v1.9.1
jQuery UI - v1.10.2
$("#myDiv").on("remove", function () {
alert("Element was removed");
})
Important: This is functionality of Jquery UI script (not JQuery), so you have to load both scripts (jquery and jquery-ui) to make it work. Here is example: http://jsfiddle.net/72RTz/
Solution 3 - Jquery
You can bind to the DOMNodeRemoved event (part of DOM Level 3 WC3 spec).
Works in IE9, latest releases of Firefox and Chrome.
Example:
$(document).bind("DOMNodeRemoved", function(e)
{
alert("Removed: " + e.target.nodeName);
});
You can also get notification when elements are inserting by binding to DOMNodeInserted
Solution 4 - Jquery
There is no built-in event for removing elements, but you can create one by fake-extending jQuery's default remove method. Note that the callback must be called before actually removing it to keep reference.
(function() {
var ev = new $.Event('remove'),
orig = $.fn.remove;
$.fn.remove = function() {
$(this).trigger(ev);
return orig.apply(this, arguments);
}
})();
$('#some-element').bind('remove', function() {
console.log('removed!');
// do pre-mortem stuff here
// 'this' is still a reference to the element, before removing it
});
// some other js code here [...]
$('#some-element').remove();
Note: some problems with this answer have been outlined by other posters.
- This won't work when the node is removed via
html()
replace()
or other jQuery methods - This event bubbles up
- jQuery UI overrides remove as well
The most elegant solution to this problem seems to be: https://stackoverflow.com/a/10172676/216941
Solution 5 - Jquery
Hooking .remove()
is not the best way to handle this as there are many ways to remove elements from the page (e.g. by using .html()
, .replace()
, etc).
In order to prevent various memory leak hazards, internally jQuery will try to call the function jQuery.cleanData()
for each removed element regardless of the method used to remove it.
See this answer for more details: https://stackoverflow.com/questions/2316726/javascript-memory-leaks
So, for best results, you should hook the cleanData
function, which is exactly what the jquery.event.destroyed plugin does:
http://v3.javascriptmvc.com/jquery/dist/jquery.event.destroyed.js
Solution 6 - Jquery
For those who use jQuery UI:
jQuery UI has overridden some of the jQuery methods to implement a remove
event that gets handled not only when you explicitly remove the given element, but also if the element gets removed from the DOM by any self-cleaning jQuery methods (e.g. replace
, html
, etc.). This basically allows you to put a hook into the same events that get fired when jQuery is "cleaning up" the events and data associated with a DOM element.
John Resig has indicated that he's open to the idea of implementing this event in a future version of jQuery core, but I'm not sure where it stands currently.
Solution 7 - Jquery
(I have extracted this extension from the jQuery UI framework) Only jQuery is required (No jQuery UI needed)
Works with: empty()
and html()
and remove()
$.cleanData = ( function( orig ) {
return function( elems ) {
var events, elem, i;
for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
try {
// Only trigger remove when necessary to save time
events = $._data( elem, "events" );
if ( events && events.remove ) {
$( elem ).triggerHandler( "remove" );
}
// Http://bugs.jquery.com/ticket/8235
} catch ( e ) {}
}
orig( elems );
};
} )( $.cleanData );
With this solution you can also unbind the event handler.
$("YourElemSelector").off("remove");
Try it! - Example
$.cleanData = (function(orig) {
return function(elems) {
var events, elem, i;
for (i = 0;
(elem = elems[i]) != null; i++) {
try {
// Only trigger remove when necessary to save time
events = $._data(elem, "events");
if (events && events.remove) {
$(elem).triggerHandler("remove");
}
// Http://bugs.jquery.com/ticket/8235
} catch (e) {}
}
orig(elems);
};
})($.cleanData);
$("#DivToBeRemoved").on("remove", function() {
console.log("div was removed event fired");
});
$("p").on("remove", function() {
console.log("p was removed event fired");
});
$("span").on("remove", function() {
console.log("span was removed event fired");
});
// $("span").off("remove");
$("#DivToBeRemoved").on("click", function() {
console.log("Div was clicked");
});
function RemoveDiv() {
// $("#DivToBeRemoved").parent().html("");
$("#DivToBeRemoved").remove();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>OnRemove event handler attached to elements `div`, `p` and `span`.</h3>
<div class="container">
<br>
<button onclick="RemoveDiv();">Click here to remove div below</button>
<div id="DivToBeRemoved">
DIV TO BE REMOVED
contains 1 p element
which in turn contains a span element
<p>i am p (within div)
<br><br><span>i am span (within div)</span></p>
</div>
</div>
Additional [Demo - jsBin][1] [1]: http://jsbin.com/pemixiz/3/edit?js,console,output [2]: http://stackoverflow.com/a/10172676/2581562
Solution 8 - Jquery
I couldn't get this answer to work with unbinding (despite the update see here), but was able to figure out a way around it. The answer was to create a 'destroy_proxy' special event that triggered a 'destroyed' event. You put the event listener on both 'destroyed_proxy' and 'destroyed', then when you want to unbind, you just unbind the 'destroyed' event:
var count = 1;
(function ($) {
$.event.special.destroyed_proxy = {
remove: function (o) {
$(this).trigger('destroyed');
}
}
})(jQuery)
$('.remove').on('click', function () {
$(this).parent().remove();
});
$('li').on('destroyed_proxy destroyed', function () {
console.log('Element removed');
if (count > 2) {
$('li').off('destroyed');
console.log('unbinded');
}
count++;
});
Here is a fiddle
Solution 9 - Jquery
I like mtkopone's answer using jQuery special events, but note that it doesn't work a) when elements are detached instead of removed or b) when some old non-jquery libraries use innerHTML to destroy your elements
Solution 10 - Jquery
I'm not sure there is an event handle for this, so you would have to keep a copy of the DOM and compare to the existing DOM in some kind of polling loop - which could be quite nasty. Firebug does this though - if you inspect the HTML and run some DOM-changes, it highlights the changes in yellow in the Firebug console for a short time.
Alternatively, you could create a remove function...
var removeElements = function(selector) {
var elems = jQuery(selector);
// Your code to notify the removal of the element here...
alert(elems.length + " elements removed");
jQuery(selector).remove();
};
// Sample usage
removeElements("#some-element");
removeElements("p");
removeElements(".myclass");
Solution 11 - Jquery
This.
$.each(
$('#some-element'),
function(i, item){
item.addEventListener('DOMNodeRemovedFromDocument',
function(e){ console.log('I has been removed'); console.log(e);
})
})
Solution 12 - Jquery
This is how to create a jQuery live remove listener:
$(document).on('DOMNodeRemoved', function(e)
{
var $element = $(e.target).find('.element');
if ($element.length)
{
// do anything with $element
}
});
Or:
$(document).on('DOMNodeRemoved', function(e)
{
$(e.target).find('.element').each(function()
{
// do anything with $(this)
}
});
Solution 13 - Jquery
The "remove" event from jQuery works fine, without addition. It might be more reliable in time to use a simple trick, instead of patching jQuery.
Just modify or add an attribute in the element you are about to remove from the DOM. Thus, you can trigger any update function, that will just ignore elements on way to be destroyed, with the attribute "do_not_count_it".
Suppose we have a table with cells corresponding to prices, and that you need to show only the last price: This is the selector to trigger when a price cell is deleted (we have a button in each line of the table doing that, not shown here)
$('td[validity="count_it"]').on("remove", function () {
$(this).attr("validity","do_not_count_it");
update_prices();
});
And here is a function that finds the last price in the table, not taking account of the last one, if it was the one that was removed. Indeed, when the "remove" event is triggered, and when this function is called, the element is not removed yet.
function update_prices(){
var mytable=$("#pricestable");
var lastpricecell = mytable.find('td[validity="count_it"]').last();
}
In the end, the update_prices() function works fine, and after that, the DOM element is removed.
Solution 14 - Jquery
We can also use DOMNodeRemoved:
$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
Solution 15 - Jquery
referencing to @David answer:
When You want to do soo with another function, eg. html() like in my case, don't forget to add return in new function:
(function() {
var ev = new $.Event('html'),
orig = $.fn.html;
$.fn.html = function() {
$(this).trigger(ev);
return orig.apply(this, arguments);
}
})();
Solution 16 - Jquery
an extension to Adam's answer incase you need to prevend default, here is a work around:
$(document).on('DOMNodeRemoved', function(e){
if($(e.target).hasClass('my-elm') && !e.target.hasAttribute('is-clone')){
let clone = $(e.target).clone();
$(clone).attr('is-clone', ''); //allows the clone to be removed without triggering the function again
//you can do stuff to clone here (ex: add a fade animation)
$(clone).insertAfter(e.target);
setTimeout(() => {
//optional remove clone after 1 second
$(clone).remove();
}, 1000);
}
});