David Walsh Blog

Detect DOM Node Insertions with JavaScript and CSS Animations

I work with an awesome cast of developers at Mozilla, and one of them in Daniel Buchner. Daniel’s shared with me an awesome strategy for detecting when nodes have been injected into a parent node without using the deprecated DOM Events API. This hack uses JavaScript, as you would expect, but another technology you wouldn’t expect: CSS animations. Let me prove to you that it works!

The HTML

All that’s required is a parent element with which we’d like to listen to node insertions within:


<ul id="parentElement"></ul>

You can use any selector helper you’d like, but I’ve chosen an ID here.

The CSS

In order to get a handle on node insertion detection, we need to set up a series of keyframe animations which will start when the node is inserted. The clip property is used since it has no effect on the node itself:


/* set up the keyframes; remember to create prefixed keyframes too! */
@keyframes nodeInserted {  
	from { opacity: 0.99; }
	to { opacity: 1; }  
}

With the keyframes created, the animation needs to be applied on the elements you’d like to listen for. Note the tiny duration; that relaxes the animation footprint on the browser.


#parentElement > li {
    animation-duration: 0.001s;
    animation-name: nodeInserted;
}

Add the animation to the child nodes you are listening for. When the animation ends, the insertion event will fire!

The JavaScript

The first step is creating an function which will act as the event listener callback. Within the function, an initial event.animationName check must be made to ensure it’s the animation name we want to listen for in this specific case:


var insertListener = function(event){
	if (event.animationName == "nodeInserted") {
		// This is the debug for knowing our listener worked!
		// event.target is the new node!
		console.warn("Another node has been inserted! ", event, event.target);
	}
}

If the animation name matches the desired animation, we know a DOM node has been injected. Now it’s time to add the event listener to the parent:


document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari

How awesomely simple is that?!

Daniel created this solution to aid in his forthcoming web components initiative, an initiative I’ll be covering more in depth very soon. This node insertion hack is useful and uses no framework, so it’s an incredible mechanism that can be used by anyone. Well done to Daniel!