Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DOM Mutation event in JQuery or vanilla Javascript

Tags:

Are there any DOM mutation events in JQuery or in vanilla Javascript that fire cross browser?

To clarify, say I have a script on my page which inserts a div into the body. I don't have access to the script and I don't know when the div has been inserted. I was wondering if there's a DOM mutation event that I can add a listener for, to know when an element has been inserted. I know I can use a timer to periodically check for the insertion but, I don't really like the overhead that this would impose.

like image 335
aziz punjani Avatar asked Oct 07 '11 20:10

aziz punjani


People also ask

What is DOM mutation?

The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which was part of the DOM3 Events specification.

What is vanilla jQuery?

Vanilla JS helped the developers in creating dynamic websites. Then came jQuery, a library of tools created by developers around the world, using Javascript. In simple words, jQuery is a lightweight and easy to use JavaScript library that helps in creating complex functionalities with few lines of coding.

How do you detect change in DOM?

“MutationObserver” is a Web API provided by modern browsers for detecting changes in the DOM. By using this API you can listen to changes in DOM, like added or removed nodes, attribute changes or changes in the text content of text nodes and make changes. Web apps are getting complex on the client-side nowadays.

How to use MutationObserver in jQuery?

For example, to monitor an element we can now write: // The node to be monitored var target = $( "#content" )[0]; // Create an observer instance var observer = new MutationObserver(function( mutations ) { mutations. forEach(function( mutation ) { var newNodes = mutation. addedNodes; // DOM NodeList if( newNodes !==


1 Answers

This is certainly a hack, but why not patch the underlying DOM methods used to insert the nodes? There are a couple ways to do this:

A. You know what specific element will be appended to:

var c = document.getElementById('#container'); c.__appendChild = c.appendChild; c.appendChild = function(){      alert('new item added');      c.__appendChild.apply(c, arguments);  } 

fiddle demo for A

B. You know what type of element will be appended to:

HTMLDivElement.prototype.__appendChild = HTMLDivElement.prototype.appendChild; HTMLDivElement.prototype.appendChild = function(){     alert('new item added');     HTMLDivElement.prototype.__appendChild(this,arguments);  } 

fiddle demo for B

(Note that solution B is not supported by IE < 8 or any other browser which does not support DOM prototypes.)

This same technique could just as easily be used on all the underlying DOM mutation functions such as insertBefore, replaceChild or removeChild.

That's the general idea, these demos could be adapted for pretty much any other use case -- say you want to cast a wide net and catch all additions regardless of type AND make sure it works across all browsers everything but IE < 8? (see example C below)


UPDATE

C. Recursively walk the DOM, swap out the function on every element to trigger a callback, and then apply the same patch to any children being appended.

var DOMwatcher = function(root, callback){   var __appendChild = document.body.appendChild;    var patch = function(node){     if(typeof node.appendChild !== 'undefined' && node.nodeName !== '#text'){       node.appendChild = function(incomingNode){         callback(node, incomingNode);         patch(incomingNode);         walk(incomingNode);         __appendChild.call(node, incomingNode);       };     }     walk(node);     };    var walk = function(node){     var i = node.childNodes.length;     while(i--){       patch(node.childNodes[i]);     }   };    patch(root);  };  DOMwatcher(document.body, function(targetElement, newElement){     alert('append detected');     });  $('#container ul li').first().append('<div><p>hi</p></div>'); $('#container ul li div p').append('<a href="#foo">bar</a>'); 

fiddle demo for C

UPDATE 2

As Tim Down commented, the above solution also won't work in IE < 8 because appendChild is not a Function and does not support call or apply. I suppose you could always fall back to the clunky but trusty setInterval method if typeof document.body.appendChild !== 'function'.

like image 159
Daniel Mendel Avatar answered Oct 26 '22 05:10

Daniel Mendel