Ask Ben: Detecting When DOM Elements Have Been Removed With jQuery

Posted June 30, 2009 at 10:11 AM by Ben Nadel

Tags: Javascript / DHTML, Ask Ben

I know that jQuery is great for event management, but I was wondering if you have come across a way to detect if a DOM element (say a row in a table) was removed? I have a table and I want to run an ajax request every time a tr was removed, but there are several ways that the tr could be removed.

If you look at the W3C, there is actually an event that gets triggered when a DOM element is removed from a document sub-tree:

DOMNodeRemoved: Fires when a node has been removed from a DOM-tree.

This DOM event will bubble up the document tree with the removed node as its target. But of course, even though this is a standard in the W3C, it's not fully supported in the various browsers. And, somewhat to be expected, from my brief testing, the one browser that I have that doesn't support this event type is Internet Explorer. However, if we are going to be using jQuery to perform our DOM mutations, we can actually simulate this event if the current browser is IE:

 
 
 
 
 
 
 
 
 
 

Before we look at the IE hack, let's just take a look at our standard test code:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>DOM Modification Event</title>
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • $(function(){
  •  
  • // Bind link handlers to remove links.
  • $( "p#children a" )
  • .attr( "href", "javascript:void( 0 )" )
  • .dblclick(
  • function( objEvent ){
  • // Remove link.
  • $( this ).remove();
  •  
  • // Cancel click event.
  • return( false );
  • }
  • )
  • ;
  •  
  • // Bind link hanlders to remove parent.
  • $( "p#nested a" )
  • .attr( "href", "javascript:void( 0 )" )
  • .dblclick(
  • function( objEvent ){
  • // Remove parent.
  • $( this.parentNode ).remove();
  •  
  • // Cancel click event.
  • return( false );
  • }
  • )
  • ;
  •  
  • // Listen to the body for any DOM modifications in
  • // which a DOM element is removed.
  • $( "body" ).bind(
  • "DOMNodeRemoved",
  • function( objEvent ){
  • // Append event to log display.
  • $( "#event-log" ).append(
  • "<li>" +
  • "Node removed: " +
  • $( objEvent.target ).text() +
  • "</li>"
  • );
  • }
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • DOM Modification Event Demo
  • </h1>
  •  
  • <p id="children">
  • <a>Remove Me 1</a>
  • <a>Remove Me 2</a>
  • <a>Remove Me 3</a>
  • <a>Remove Me 4</a>
  • </p>
  •  
  • <p id="nested">
  • Child Action: <a>Remove my parent</a>
  • </p>
  •  
  • <h2>
  • Event Log
  • </h2>
  •  
  • <ul id="event-log" />
  •  
  • </body>
  • </html>

As you can see, in the first event binding, we are telling the double-click event to remove the given link from the document. Then, in the second event binding, we are telling the double-click event to remove the given parent element (of the clicked link) from the document. So far, this is just standard jQuery code; it's the third event binding that gets interesting. Here, we are binding an event listener to the BODY tag to listen for the "DOMNodeRemoved" event. Since the DOMNodeRemoved event bubbles up through the parent DOM, the body tag will be able to listen for any of the elements being removed in its tree.

Once we have this event listener bound to the BODY, we are catching the events and outputting the text() of the target node (the one being removed) for debugging.

Now, the DOMNodeRemoved event fires implicitly for all the Mozilla / Safari based browsers (it seems). But, like I said before, IE does not want to cooperate. As such, we have to do a little fenagling and actually fire the DOMNodeRemoved event explicitly. While irritating, this is actually easy so long as we are using jQuery to do all of our DOM mutation (which I assume most of us are by now). All we have to do is modify the remove() function in the jQuery library:

  • remove: function( selector ) {
  • if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
  • // Prevent memory leaks
  • jQuery( "*", this ).add([this]).each(function(){
  • jQuery.event.remove(this);
  • jQuery.removeData(this);
  • });
  •  
  • // -------------------------------------------- //
  • // If this is IE, then manually trigger the DOM
  • // node removed event on the given element.
  • if (jQuery.browser.msie){
  • jQuery( this ).each(function(){
  • jQuery( this ).trigger({
  • type: "DOMNodeRemoved"
  • });
  • });
  • }
  • // -------------------------------------------- //
  •  
  • if (this.parentNode)
  • this.parentNode.removeChild( this );
  • }
  • }

As you can see, I've added just a few lines of logic to jQuery's remove() method which states that if the current browser is MSIE, then manually trigger the "DOMNodeRemoved" event on the target element. Since jQuery automatically bubbles events through the DOM, this event will now bubble up to the BODY tag at which point it will trigger the DOMNodeRemoved event listener that we bound earlier. And, since we triggered the event on the target element, the Target property of the event object will be the same in IE as it is in the Mozilla browsers.

I have not tested this a whole lot, but this solution seems to work well in Firefox, Chrome, Safari, and Internet Explorer. I hope this helps or at least points you in the right direction.


You Might Also Be Interested In:



Reader Comments

Jun 30, 2009 at 10:24 AM // reply »
66 Comments

Nice job, Ben!


Jun 30, 2009 at 10:28 AM // reply »
11,238 Comments

@Hal,

Thanks! It was an interesting question that was asked.

Actually, looking at the code, the each() and the trigger() are redundant (as trigger will apply to each element in the set). The code can be reduced to:

if (jQuery.browser.msie){
. . . jQuery( this ).trigger({
. . . . . . type: "DOMNodeRemoved"
. . . });
}


Jun 30, 2009 at 11:17 AM // reply »
42 Comments

That is pretty darn awesome! I definitely need to spend some more time over at the w3c. Of course, so do the guys at Microsoft...

Thanks so much for taking the time on this one!


Jun 30, 2009 at 11:18 AM // reply »
11,238 Comments

@Brandon,

No problem at all. Hope this helps a bit.


Jun 30, 2009 at 12:01 PM // reply »
1 Comments

is there a way to overload the included remove() function in jquery? possibly a seperate js file? for example we are using the min version and it would be cool to not have to add this in each time we upgraded to a new version that has come out?

thanks,
Paul S.


Jun 30, 2009 at 2:11 PM // reply »
11,238 Comments

@Paul,

Take a look at this:

http://www.bennadel.com/blog/1624-Ask-Ben-Overriding-Core-jQuery-Methods.htm


Jun 30, 2009 at 7:54 PM // reply »
16 Comments

well done Ben. Pretty nifty


Pat
Jul 22, 2009 at 11:44 AM // reply »
1 Comments

Hi,

I'm trying to do the same thing with the DOMAttrModified and the attr jquery function.
But in IE i can't get the "attrName" and "newValue".

Do you have any ideas ?

Thanks !


Jul 27, 2009 at 1:37 PM // reply »
11,238 Comments

@Pat,

You'd probably have to do the same thing that we did with the remove() method, but with the attr() and removeAttr() methods.


Aug 19, 2009 at 10:16 AM // reply »
1 Comments

Unfortunately it's does not help if changing innerHTML or innerText or using DOM methods.
Do you have any ideas ?


Sep 6, 2009 at 1:55 PM // reply »
11,238 Comments

@Andrei,

Yeah, it will only work when you use the remove() jQuery methods.


don
Jun 15, 2010 at 12:28 PM // reply »
1 Comments

It's a good start but the problem is this:

1. Element "A" is a child of "P"
2. I append element "A" to another element "B"
3. The browser's DOM automatically removes "A" from "B" (since an element cannot be a child of two elements)
4. I never receive a remove event


Jun 16, 2010 at 9:01 AM // reply »
11,238 Comments

@Don,

Hmmm. I am not sure if there is anything you can do about that.


Nov 18, 2010 at 1:24 PM // reply »
1 Comments

I have different type of problem. I am creating a Javascript class using Jquery. One function creates DOM elements and append to particular divID. I presume that once appended it is available in the document. But when I try to access the new element appended from another function it gives null. I have created 2 div of class name ".widget" with first function. with the second function I want to iterate say $('.widget').each(). But there is no element . Why is this problem arises.


Apr 20, 2012 at 4:03 AM // reply »
4 Comments

Since the DOMNodeRemoved event is supported in IE9, I think it is a good idea to change

if (jQuery.browser.msie)

into

if ($.browser.msie && $.browser.version < 9)

to ensure that the event does not fire twice in IE9



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 19, 2013 at 2:31 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
It's funny really just how well that image describes the way I would imagine most people that go with angular for some project is. I have had a similar roller-coaster ride with it as well, but not qu ... read »
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools