[jquery] jQuery - Trigger event when an element is removed from the DOM

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.

This question is related to jquery events dom triggers

The answer is


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");

Only jQuery is required (No jQuery UI needed)

(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

_x000D_
_x000D_
$.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_
_x000D_
_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.

  1. This won't work when the node is removed via html() replace() or other jQuery methods
  2. This event bubbles up
  3. jQuery UI overrides remove as well

The 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.


Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to events

onKeyDown event not working on divs in React Detect click outside Angular component Angular 2 Hover event Global Events in Angular How to fire an event when v-model changes? Passing string parameter in JavaScript function Capture close event on Bootstrap Modal AngularJs event to call after content is loaded Remove All Event Listeners of Specific Type Jquery .on('scroll') not firing the event while scrolling

Examples related to dom

How do you set the document title in React? How to find if element with specific id exists or not Cannot read property 'style' of undefined -- Uncaught Type Error adding text to an existing text element in javascript via DOM Violation Long running JavaScript task took xx ms How to get `DOM Element` in Angular 2? Angular2, what is the correct way to disable an anchor element? React.js: How to append a component on click? Detect click outside React component DOM element to corresponding vue.js component

Examples related to triggers

How to get JQuery.trigger('click'); to initiate a mouse click insert/delete/update trigger in SQL server How do you change Background for a Button MouseOver in WPF? mysql after insert trigger which updates another table's column creating triggers for After Insert, After Update and After Delete in SQL Why is the GETDATE() an invalid identifier Declare variable MySQL trigger How to emulate a BEFORE INSERT trigger in T-SQL / SQL Server for super/subtype (Inheritance) entities? MySql Error: Can't update table in stored function/trigger because it is already used by statement which invoked this stored function/trigger jQuery click anywhere in the page except on 1 div