Friday, May 3, 2013

JavaScript & DOM Reflows

js45_400

This article is from the November 2012 issue of the JSMag publication.

In here, we discuss the concept of DOM reflows and why they are expensive (and should be avoided).

I am replicating the article in its entirety as below and not to mention with full input & collaboration-credit to my good friend Chandan Luthra!

Hope it is helpful to my friends out there!


If you use JavaScript for your web development, you need to know about DOM reflows. Here we discuss what reflows are and why they happen. We also go through ways to minimize reflow issues in order to improve java-script performance.

 

What is a reflow? – an overview

Let us start evaluating a simple HTML or XHTML webpage. This webpage is bound to include references to resources such as CSS, external java scripts (JS) and images etc. and all these collectively contribute to the final look and feel of the webpage UI.

These relative DOM elements are connected in a hierarchical/structural layout, and are encompassed in a logical pattern called a frame. This frame may be a child or a parent of subsequent inner frame element(s). Each of these rectangular frames has specific attributes like width / height and so on.

During the rendering process of the webpage, the entire DOM element model is traversed by the browser. This traversal starts from the absolute parent (i.e. the <html> element) then proceeds or flows through the entire DOM. A reflow refers to the revalidation of page elements for (re)rendering the entire DOM after any DOM updates.

 

Wait a minute! How is this different from a repaint?

Both DOM reflow and repaint events can happen on an HTML page and reveal any element’s altered state. A repaint occurs when changes are made purely at a superficial or visible level, like changing the color attributes of an element, and which do not affect element layout at all. Examples of this can include modifying table borders, div related visibility or page /element level background color.

A repaint too incurs some expense as the browser engine has to re-process the visibility criteria for the whole DOM tree.

 

When does a reflow happen?

A reflow is more or less quite a substantial change and is triggered whenever a user or script action makes changes to the DOM tree.

This may be due to changing a style (thereby changing layouts), changing any element’s className property or simply changing browser level properties, like the browser’s window size etc.

As soon as such a change is detected by the browser engine, the actual changed element and its subsequent child elements have to undergo a reflow so as to cascade their parent’s update.

This change is cascaded further down the line and all the dependent elements appearing after the updated DOM element have to be reflowed in order to adjust to the newly calculated layout.

Moreover, the parent or preceding elements will also need a reflow to acknowledge the layout changes of their child elements. And after this reflow is implemented all over, a final repaint request is issued for the DOM.

Reflows can be classified as instant (as in case of a response to user / script processing) or they may be required as a part of page load process, when page elements are loaded in a sequence (for example in stream based rendering).

 

So why are reflows actually bad?

Sometimes reflows are actually required. An example is using script based animation sequences which is handled via reflows. But an “un-intentional reflow” due to coding issues in scripts and poorly planned style classes is what leads to expensive and performance hampering behavior.

Reflows are bad since they are very expensive and counterintuitive to performance. They can and do lead to slow JavaScript and a poor user experience. The scenario gets even worse for devices with low processing power such as phones and tablets or any other device where processing power is at a premium. A reflow is more or less akin to refreshing or performing a fresh layout for the entire page, which is surely heavy and undesired.

 

Some common reflow events and how to minimize them

Let us go through some easily avoidable reflow instances which are mainly caused due to coding oversight.

 

Updating DOM elements in a loop

Assume a scenario where-in we need to modify an element in the DOM tree. For example, suppose we have a DIV parent having some child anchor tags and we need to change the className attribute for these anchor tags within this DIV element.

function updateDivAnchors(divElement, newAnchorClass){
var anchorObjs = divElement.getElementsByTagName('a');
var totalAnchorObjs = anchorObjs.length;
for (var i = 0; i < totalAnchorObjs; i ++) {
anchorObjs[i].className = newAnchorClass;
}
}

Listing 1: User defined method that replaces/adds a className to the div elements


At a glance, the function in Listing 1 will surely do what is intended, but we have a major reflow issue here. This code fires a DOM reflow for every update to each anchor element (as we have a loop in effect).

Now, if this DIV has 10 child anchor elements, the entire DOM tree would undergo an un-necessary refresh 10 times. This is a lot of undesired reflow activity!

Any better solution should avoid touching the DOM during the DIV update. One way this can be achieved is by using a modified script that would –


1) Remove the DIV (to be updated) from the DOM;

2) Update the anchor elements in that DIV;

3) Re-add the DIV back at its location in the DOM.


This approach can be further refined if we make use of the DocumentFragment object.

As per this definition at Mozilla Developer,


A DocumentFragment is a minimal document object that has no parent. It is used as a light-weight version of document to store well-formed or potentially non-well-formed fragments of XML.


Thus, a DocumentFragment can be created on the fly to create a temporary DIV node as shown in Listing 2.


1 var docFragment = document.createDocumentFragment();
Listing 2: Example to create DocumentFragment object on the fly

After this, we can iterate and populate this fragment, manipulate the anchor elements, and add back (rather replace) the actual DIV with this updated fragment. So basically, the number of reflows which was proportional to the number of anchor tags in the previous example is now limited to just two reflows! This is bound to speed up the action for that page.

 

Manipulating visibility attribute to update element properties


Another way to reduce reflows is to use hidden elements. In case an element or its children require many separate changes (which cannot be combined into a single repaint), the element’s visibility style can be set as display:none, the changes made, and then the element can be set back to its normal display.

Changing properties or attributes of hidden elements does not trigger the repaint request since it is not being displayed.

Similar to the earlier mentioned fix, in this case too we need only two reflows. One when the element is hidden, and second when it is made to appear again, but the overall effect can be much faster (issues like abrupt UI refresh for dependent elements can be avoided by using a preset offset value)

 


Changing an element’s style attributes – style vs. class update


If we need to make several style related updates for an element, the same should be done at one go to minimize the number of reflows.

We generally code to set element styles one at a time, as shown in Listing 3.


1 var targetElement = document.getElementById('anyDivId');
2 targetElement.style.background = '#333';
3 targetElement.style.color = '#fff';
4 targetElement.style.border = '1px solid #00f';
5
Listing 3: Code to change/update few cascade styles of an HTML element

Here, for every single style update a new reflow request is invoked which is again an overhead for the DOM. A better and faster approach would be to create a CSS class for the identified style changes and update the same as shown in Listing 4.


1 document.getElementById(' anyDivId ').className = 'newDivClass';
Listing 4: Example of applying CSS class from JavaScript

Note: There are many more ways which can be utilized for further minimizing the nuisance due to reflows. Please refer the links at the end of the article for the same.

 


Figures to back the impact of reflows


The point of the entire discussion is that it is actually JavaScript which triggers the reflows and repaints. Avoiding the reflows will make the page appear faster not because of a speedier script engine but because the browser is doing less of reflow and repaint activity.

The reflow minimization above may or may not lead to any tangible/actual performance benefits (like JavaScript speed up etc.). But the advantage will be that the browser will now show a faster response time against actual user actions. We will enable the browser in such a manner so that it spends lesser time requesting expensive I/O and processor resources for redundant DOM reflows.

If we use any pure JavaScript profiling tool, the reflow related code optimization will show little or no affect. We are not really bothered about speeding up the JS code; rather, we need to measure the effect it has on the browser rendering for that modified page.

As a reference, the link below shows the reflow in action (Wikipedia’s home page reflow video) –

http://video.google.com/videoplay?docid=-5863446593724321515&hl=en

If you want to track the paint events for your webpage, you need Firebug (https://addons.mozilla.org/en-us/firefox/addon/firebug/) and this excellent add-on https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/.

This will show the DOM reflow/paint events in the Firebug console.

 


Conclusion & follow up


The above primer will surely be helpful for you in minimizing un-necessary reflow events. Below are some links for pursuing the reflow discussion further:

· A helpful note on HTML reflowhttp://www-archive.mozilla.org/newlayout/doc/reflow.html

· When does JavaScript trigger reflows and rendering?http://mir.aculo.us/2010/08/17/when-does-javascript-trigger-reflows-and-rendering/

· Page Rendering & Reflow Performancehttp://blog.monitor.us/2012/04/page-rendering-reflow-performance/

· CSS solutions for Browser Reflows & Repaints & how do they affect performancehttp://ajaxian.com/archives/browser-reflows-how-do-they-affect-performance




Post a Comment