DOM

FastDom: eliminate layout thrashing by batching DOM measurement and mutation tasks

FastDom works as a regulatory layer between your app/library and the DOM. By batching DOM access we avoid unnecessary document reflows and dramatically speed up layout performance.

Each measure/mutate job is added to a corresponding measure/mutate queue. The queues are emptied (reads, then writes) at the turn of the next frame using window.requestAnimationFrame.

FastDom aims to behave like a singleton across all modules in your app. When any module requires 'fastdom' they get the same instance back, meaning FastDom can harmonize DOM access app-wide.

Document.createDocumentFragment()

Creates a new empty DocumentFragment into which DOM nodes can be added to build an offscreen DOM tree.

[…]

DocumentFragments are DOM Node objects which are never part of the main DOM tree. The usual use case is to create the document fragment, append elements to the document fragment and then append the document fragment to the DOM tree. In the DOM tree, the document fragment is replaced by all its children.

Since the document fragment is in memory and not part of the main DOM tree, appending children to it does not cause page reflow (computation of element’s position and geometry). Historically, using document fragments could result in better performance.

Code language: JavaScript

var element  = document.getElementById('ul'); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera', 
    'Safari', 'Internet Explorer'];
 
browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});
 
element.appendChild(fragment);

Taming huge collections of DOM nodes

Hajime Yamasaki Vukelic has come to the conclusion that, if you’re dealing with a really big number of DOM nodes that need to be updated in real time, frameworks are usually incredibly slow on top of the already-slow DOM operations you have to do. The solution they settled on is to avoid frameworks altogether for these scenarios and use vanilla JavaScript. Also of note are that repaints and reflows are going to be big bottlenecks regardless of what you use.

  • If you are looking for performance, don’t use frameworks. Period.
  • At the end of the day, DOM is slow.
  • Repaints and reflows are even slower.
  • Whatever performance you get out of your app, repaints and reflows are still going to be the last remaining bottleneck.
  • Keep the number of DOM nodes down.
  • Cache created DOM nodes, and use them as a pool of pre-assembled elements you can put back in the page as needed.
  • Logging the timings in IE/Edge console is unreliable because the developer tools have a noticeable performance hit.
  • Measure! Always measure performance first, then only fix the issues you’ve reliably identified.

How to determine which, if any CSS rules are unused on a site

So this is a really interesting way to determine which, if any CSS rules are unused in a stylesheet, site-wide:

Part of this story could certainly be about deleting CSS that is determined to be “unused” in a project. I know there is incredible demand for this kind of tooling. I feel like there are some developers damn near frothing at the mouth to blast their CSS through some kind of fancy tool to strip away anything unneeded.

[…]

Here’s how one company I heard from was doing it:

  1. They injected a script onto the page for some subset of users.
  2. The script would look at the CSSOM and find every single selector in the CSS for that page.
  3. It would also run a querySelectorAll(“*”) and find every single DOM node on that page.
  4. It would compare those two sets and find all selectors that seemed to be unused.
  5. In order to get the best results, it would fire this script after a random amount of seconds, on a random set of users, in a random set of conditions. Even with this, it needed a lot of data over a long period of time.
  6. After that had run for long enough, there was a set of CSS selectors that seemed likely to be unused.
  7. To be sure, unique background images were applied to all those selectors.
  8. After applying those and waiting for another length of time, the server logs were checked to make sure those images were never accessed. If they were, that selector was used, and would have to stay.
    Ultimately, the unused selectors could safely be deleted from the CSS.

Whew! That’s an awful lot of work to remove some CSS.

But as you can imagine, it’s fairly safe. Imagine just checking one page’s CSS coverage. You’ll definitely find a bunch of unused CSS. One page, in one specific state, is not representative of your entire website.

hyperHTML: A Virtual DOM Alternative

The easiest way to describe hyperHTML is through an example.

Code language: JavaScript

// this is React's first tick example
// https://facebook.github.io/react/docs/state-and-lifecycle.html
function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}
setInterval(tick, 1000);
 
// this is hyperHTML
function tick(render) {
  render`
    <div>
      <h1>Hello, world!</h1>
      <h2>It is ${new Date().toLocaleTimeString()}.</h2>
    </div>
  `;
}
setInterval(tick, 1000,
  hyperHTML.bind(document.getElementById('root'))
);

Features

  • Zero dependencies and it fits in less than 2KB (minzipped)
  • Uses directly native DOM instead of inventing new syntax/APIs, DOM diffing, or virtual DOM
  • Designed for template literals, a templating feature built in to JS
  • Compatible with vanilla DOM elements and vanilla JS data structures *
  • Also compatible with Babel transpiled output, hence suitable for every browser you can think of

* actually, this is just a 100% vanilla JS utility, that’s why is most likely the fastest and also the smallest. I also feel like I’m writing Assembly these days … anyway …

Understanding the Critical Rendering Path

When a browser receives the HTML response for a page from the server, there are a lot of steps to be taken before pixels are drawn on the screen. This sequence the browsers needs to run through for the initial paint of the page is called the “Critical Rendering Path”.

Knowledge of the CRP is incredibly useful for understanding how a site’s performance can be improved. There are 6 stages to the CRP -

  1. Constructing the DOM Tree
  2. Constructing the CSSOM Tree
  3. Running JavaScript
  4. Creating the Render Tree
  5. Generating the Layout
  6. Painting