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
});
thanks.
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: 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
This.
$.each(
$('#some-element'),
function(i, item){
item.addEventListener('DOMNodeRemovedFromDocument',
function(e){ console.log('I has been removed'); console.log(e);
})
})
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
We can also use DOMNodeRemoved:
$("#youridwhichremoved").on("DOMNodeRemoved", function () {
// do stuff
})
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");
(I have extracted this extension from the jQuery UI framework)
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) {_x000D_
return function(elems) {_x000D_
var events, elem, i;_x000D_
for (i = 0;_x000D_
(elem = elems[i]) != null; i++) {_x000D_
try {_x000D_
_x000D_
// Only trigger remove when necessary to save time_x000D_
events = $._data(elem, "events");_x000D_
if (events && events.remove) {_x000D_
$(elem).triggerHandler("remove");_x000D_
}_x000D_
_x000D_
// Http://bugs.jquery.com/ticket/8235_x000D_
} catch (e) {}_x000D_
}_x000D_
orig(elems);_x000D_
};_x000D_
})($.cleanData);_x000D_
_x000D_
_x000D_
$("#DivToBeRemoved").on("remove", function() {_x000D_
console.log("div was removed event fired");_x000D_
});_x000D_
_x000D_
$("p").on("remove", function() {_x000D_
console.log("p was removed event fired");_x000D_
});_x000D_
_x000D_
$("span").on("remove", function() {_x000D_
console.log("span was removed event fired");_x000D_
});_x000D_
_x000D_
// $("span").off("remove");_x000D_
_x000D_
$("#DivToBeRemoved").on("click", function() {_x000D_
console.log("Div was clicked");_x000D_
});_x000D_
_x000D_
function RemoveDiv() {_x000D_
// $("#DivToBeRemoved").parent().html(""); _x000D_
$("#DivToBeRemoved").remove();_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<h3>OnRemove event handler attached to elements `div`, `p` and `span`.</h3>_x000D_
<div class="container">_x000D_
<br>_x000D_
<button onclick="RemoveDiv();">Click here to remove div below</button>_x000D_
<div id="DivToBeRemoved">_x000D_
DIV TO BE REMOVED _x000D_
contains 1 p element _x000D_
which in turn contains a span element_x000D_
<p>i am p (within div)_x000D_
<br><br><span>i am span (within div)</span></p>_x000D_
</div>_x000D_
</div>
_x000D_
Additional Demo - jsBin
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
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.
html()
replace()
or other jQuery methodsThe most elegant solution to this problem seems to be: https://stackoverflow.com/a/10172676/216941
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)
}
});
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
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);
}
})();
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.
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);
}
});
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') { ... }
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.
Source: Stackoverflow.com