O'Reilly

Detect DOM Node Insertions with JavaScript and CSS Animations

By on  

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!

Track.js Error Reporting

Upcoming Events

Recent Features

  • How I Stopped WordPress Comment Spam

    I love almost every part of being a tech blogger:  learning, preaching, bantering, researching.  The one part about blogging that I absolutely loathe:  dealing with SPAM comments.  For the past two years, my blog has registered 8,000+ SPAM comments per day.  PER DAY.  Bloating my database...

  • 6 Things You Didn&#8217;t Know About Firefox OS

    Firefox OS is all over the tech news and for good reason:  Mozilla's finally given web developers the platform that they need to create apps the way they've been creating them for years -- with CSS, HTML, and JavaScript.  Firefox OS has been rapidly improving,...

Incredible Demos

Discussion

  1. Rolf

    Nice hack.. especially with all the vendor prefixes in the css :)

    You mention the DOM events API is deprecated, but not all? https://developer.mozilla.org/en/Mozilla_event_reference shows DOMContentLoaded is not deprecated?

    Cool post.

    • Fairly certain DOMContentLoaded will stay; the others are taxing on the browser.

    • To be specific: DOM Level 3 Mutation events are the ones that got the deprecation ax ;)

  2. Don’t know much about jquery. Can’t think this is possible using jquery! Gonna to try today, yeahhh :)

    • Daniel

      Yeah, this is just vanilla JS, it works all by itself, no framework required – and for the record, jQuery is not magical-unicorn-pixie code, it can’t do a single thing JavaScript itself can’t do…because it IS JavaScript.

  3. sasklacz

    why shouldn’t it be ? Whole javascript here is just adding event listeners.

  4. Me

    Very-very sneaky, sir :)

  5. Clever. A couple of questions:

    1. What is the overhead of this technique?
    2. I would assume that browsers consider animations as optional, so they can be skipped under heavy load. Has this hack been rigorously tested for any missed insertions?

  6. Might be useful to also include a link to Daniel’s original post in the article.
    http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

  7. Pablo P.

    How to apply this to $(document).on("nodeinserted",".selector",handler); ?

    use nodeinserted just like any other event that works with jQuery on?

    It could be very useful to target elements that were inserted, and its event being insertion rather than interactions such as click or focus. Thank you

  8. I just came to say that this is brilliant. You’re a genius. It’s a shame that IE9 doesn’t support CSS Keyframes as this technique would open the possibility to make a proper CSS Keyframes polyfill.

    Keep up the good work!

  9. Just want to say thanks man, this is very helpful to our team and a beautiful solution. Previously I was just polling the DOM… =p

    Thanks again!

  10. Clever approach. With regards to the CSS, and if I’m not mistaken, it’s typically best to place the unprefixed properties after the prefixed properties so that the unprefixed implementation overrules the older prefixed implementation in browsers that support both for backward-compat.

    Fantastic solution to a very common problem though.

  11. This technique is very useful in browser extensions, for example adding a widget to gmail (something like rapportive). In gmail, nearly the entire environment is rendered via javascript and well after window.onload event. Your extension environment is also completely sandboxed so you can’t bind to any of gmail’s javascript events.

    The only thing you have access to is the DOM and this technique works beautifully! The response time is near instant — very well done, thank you!

    • Dan

      Thank you, Jade, for this comment. I’m building my very first app (a Chrome extension for gmail) and this comment helped me realize this is the technique I likely need to use.

  12. Jesse

    pretty neat. i left console up on your demo and came back to the page and nodes were still getting inserted even when on another page?!

  13. @Jesse: the setInterval (or MooTools or JavaScript equivalent; I’m a jQuery kinda guy) function still fires even when you leave the tab, as long as the page is open in the browser the event will fire, because you were inspecting that page it showed up in console even though your active tab was back here.

  14. Why are mutation events deprecated anyway, when there’s nothing but hacks to replace them?

    BTW DOMContentLoaded is not a mutation event, it just sounds like one because of its name

  15. Did you notice that you have used following for IE:

    document.addEventListener("MSAnimationStart", insertListener, false); 
    

    addEventListener ain’t for IE

    Nice trick however mate. I think we can have a growl notification with a bit CSS3 animation for DOM node insertion update

  16. Captain Dingleberry

    Anyone got this to work in IE10? Looks like it does not support animation of the clip property (based on this : http://stackoverflow.com/questions/11431439/msanimationstart-event-doesnt-work-on-ie10) , so the animation start event does not fire. Any other properties one could animate that does not force a reflow of the page?

  17. I got it to work in IE10. And made it a library some time ago.

    https://github.com/naugtur/insertionQuery

    It also allows selectors etc.

  18. joan

    IE 9 doesn’t support CSS animations. And as I know there’re also mutation events and mutation observers designed for DOM mutation events.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Generating Alternative Stylesheets for Browsers Without @media

    If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the...

  • Serve a Directory with PHP

    Many developers have a giggle at PHP, even looking down at the language, but let's be honest:  most of our blogs are powered by it (WordPress) and it's a great language to dabble around with.  I cut my teeth on PHP, though I prefer to avoid PHP these days. But...

  • Tips you can Use to Build an Excellent eCommerce Experience

    There are many reasons to build a good eCommerce experience if you are planning to build an eCommerce website. Visitors can place items in their cart and leave your site without making a purchase. In fact, three out of every four of them will do so....

  • OSCON: Live Stream

    O'Reilly's OSCON is upon us and I'm super excited to share with you a live stream of OSCON keynotes, courtesy of O'Reilly! Check back here every morning this week to get a sneak peek at the best conference in the open source community! Thank you...

  • OâReilly Velocity Conference â New York

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...