JavaScript performance – avoiding reflow

The DOM API is the worst part of writing web applications. Besides being partially implemented and nonsensical, its awfully slow. If you have a large application that accesses the DOM frequently, you’re likely to need some techniques to prevent your pages becoming sluggish.

One of these techniques is avoiding page reflow. Reflow is the process by which the browser recalculates the position of elements on the page. Reflow is necessary in many situations; when the page is first loaded, or when the user resizes the browser window, for example. It is also triggered when you change a style attribute on a DOM element.

The trouble is, reflow costs a lot in terms of performance. Some browsers try to compensate for this by detecting when reflow really is needed, but this is patchy at best. If you can write your scripts to avoid unnecessary reflow, your application will thank you.

The direct approach

Let’s consider an example. With Ignite (or just about any JS library) you can easily set styles on multiple elements, using something like this:

ignite('a').setStyle({"color":"green","fontWeight":"bold"});

Here, we first get references to every anchor in the document, then apply a couple of inline styles to each of those elements. This is inefficient for two reasons; we are querying the DOM to reference the anchors, then we are triggering reflow twice for each anchor, as each inline style is applied in turn.

In fact, on a page with 300 anchors, this took on average 63ms for Firefox to complete. Let’s get that number down.

With classes

The most obvious solution is to have a CSS declaration in place, such as a class, that we can apply to the anchors and trigger the style changes we need. This reduces the number of reflows, as we are applying the change all at once:

.foo {color:green;font-weight:bold}
...
ignite('a').addClass('foo');

On the same test page, this technique brought the time down by around 50%, to 33ms. Not bad – but we can still do better.

Stylesheets

The above technique is commonly used, but it isn’t perfect. There are many times when we may not know the CSS values beforehand – we can’t describe class rules in a CSS file is the values are calculated at runtime.

The solution is to rewrite the stylesheet at runtime instead. This isn’t a simple task, because as was mentioned at the top of the article, DOM implementations tend to differ between browsers. Ignite 0.13 has this ability wrapped up in a neat method, meaning you can apply rules to a selector at any point in your code.

ignite.style.addCSS('a',{"color":"green","font-weight":"bold"});

This approach has two advantages; reflow is limited as the styles are applied at once, but we don’t need to add a class to trigger the update change. The upshot is that we can change styles of 300 elements without touching the elements themselves with script. The only DOM access we need is the stylesheet. Final time with this technique?

2ms.

Job done!

Posted October 3rd, 2009 in CSS, Ignite, JavaScript.

Leave a response: