I’ve been looking into ways to modify elements before DOMContentLoaded so that enhancements can be made as an element is rendered to the screen allowing users to begin interacting with a page even if it hasn’t finished loading or if rendering is being blocked by 3rd party assets such as banner ads or tracking code. The most robust way to do this is to include an inline <script> directly after each element you need to enhance which calls the relevant JavaScript functions — this works, but it’s not very DRY and it’s not easy to maintain.

Recently, I stumbled across a hack in an article by David Walsh which uses a CSS animation and the animationStart event to detect when an element has been inserted into the DOM. It works by exploiting the fact that CSS animations run as soon as an element is appended to the document. Creating a simple animation that runs for a tiny duration and applying it to any elements you to want modify will fire a animationStart event for each element as it’s inserted, essentially emulating the (now defunct) DOM mutation events. What’s great about this technique is it also works during page load!

The trick

Let’s say we want to enhance all <input> fields:

<input type="text" size="50">

We’ll need to set up a CSS animation and apply it to every <input> field with CSS:

/* prefixes omitted for brevity */
input {
    animation-name: nodeReady;
    animation-duration: 0.001s;
}

@keyframes nodeReady {  
    from { clip: rect(1px, auto, auto, auto); }
    to { clip: rect(0px, auto, auto, auto); }  
}

Finally, we need to make our “enhancements” with JavaScript:

/* prefixed variants omitted for brevity */
document.addEventListener("animationstart", function(e) {
    if (e.animationName == "nodeReady") {
        e.target.value = new Date();
    }
}, false);

That’s it. If the browser supports CSS animations all <input> elements will be modified as the page loads.

Fallbacks

If the browser doesn’t support CSS animations we need a fallback to ensure these elements are still modified. Handling this case is actually really simple, just use DOMContentLoaded or the window load e!vent to fire the code you would have run in the animationStart event. In this situation things will behave the way they always have.

Credit

Credit must go to David Walsh for his article and to Daniel Buchner for the original discovery. Also, James Allardice is working on wrapping this up into something easily deployable.

Personal Achievements

  • 2015 JS1k – winner
  • 2014 Net Awards: Demo of the year – winner
  • 2014 Net Awards: Developer of the year – longlist
  • 2013 Public speaking for the first time
  • 2011 .net Magazine innovation of the year – shortlist

Referenced in…

Smashing CSS, CSS3 for web designers, Programming 3D Applications with HTML5 and WebGL and more.

I am very proud to have had my work referenced in a number of publications, written by some very talented people.