preface

Most modern Web frameworks are data-driven, such as React and Vue, so developers can modify data to drive interface updates without directly touching the DOM. But as a front-end engineer, it’s important to understand browser redrawing and rearranging to help you write better performing Web applications.

Browser rendering

  • CSS Tree: The browser interprets the CSS into a CSSOM Tree structure
  • DOM Tree: A data structure that the browser parses HTML into a Tree
  • Render Tree: Combine DOM and CSSOM into a Render Tree

With the Render Tree, the browser knows which nodes are in the page and how they relate to CSS, thus knowing the location and geometry of each node, and then drawing the page.

Redraw and rearrange

When a DOM change affects an element’s geometry, such as width and height, it causes the browser to recalculate the element’s geometry, as well as other elements that are also affected by the element. At this point, the browser invalidates the affected portion of the render tree and reconstructs the render tree. This process is called rearrangement (also known as “reflow”), and when the rearrangement is complete, the browser redraws the affected portion of the page. This process is called repaint.

So rearrangement must cause redrawing, and redrawing does not necessarily cause rearrangement, for example, a change in an element does not affect the layout change (background-color change), in which case only one redraw will occur (no rearrangement is required).

Factors causing the rearrangement

It can be concluded that rearrangements can occur when elements’ geometry or page layout changes, such as:

  • Operations on visible DOM elements (add, remove, or change order)
  • The position of the element changes
  • The geometry of an element changes (e.g., margins, inner margins, border widths, and changes in width and height due to content changes)
  • Page first render
  • Pseudoclass style activation (hover, etc.)
  • Browser viewport size changed (scrolling or zooming)

How to optimize

Redrawing and rearrangement are expensive operations (because each rearrangement incurs computational overhead), and they can cause the UI of a Web application to become unresponsive, so developers should try to minimize these processes when writing applications.

Render tree queue

Because too much redrawing and rearranging can cause an application to lag, browsers have an optimized process for this. Most browsers queue for batch execution (for example, putting script changes to the DOM in a queue and drawing again after all the queue operations have finished). However, developers may sometimes unknowingly force a refresh of the render queue to rearrange and redraw immediately. For example, obtaining page layout information will force a refresh of the render queue. The following properties or methods will trigger the page drawing immediately:

  • OffsetTop, offsetLeft, offsetWidth, offsetHeight
  • ScrollTop, scrollLeft, scrollWidth, scrollHeight
  • ClientTop, clientLeft, clientWidth, clientHeight
  • getComputedStyle()

All of the above attributes and methods require the browser to return the latest layout information, so the browser immediately performs “pending changes” in the render queue, triggers a rearrangement redraw and returns the latest values. Therefore, you should avoid using these attributes and methods when modifying styles.

Reduce redrawing and rearrangement

To reduce the number of redraws and rearrangements, developers should combine DOM changes and style changes multiple times and then process them all at once.

Merge style operation

Such as:

var el = document.querySelector('div');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
Copy the code

Can be combined into:

var el = document.querySelector('div');
el.style.cssText = 'border-left: 1px; border-right: 1px; padding: 5px; '
Copy the code

Batch modify DOM

Taking elements out of the document flow, manipulating them, and then bringing them back into the document can significantly reduce the number of redraws and rearrangements. There are three basic ways to get elements out of the document flow:

Hide elements, apply changes, and redisplay
var ul = document.querySelector('ul');
ul.style.display = 'none';
// code... Perform DOM operations on ul
ul.style.display = 'block';
Copy the code
Using document fragments, build a blank document for DOM manipulation and then put it back into the original document

var fragment = document.createDocumentFragment();
// code... DOM operations on fragments
var ul = document.querySelector('ul');
ul.appendChild(fragment)
Copy the code
Copy the element to be modified to a node out of the document flow, modify the copy, and then replace the original element
var ul = document.querySelector('ul');
var cloneUl = ul.cloneNode(true);
// code... DOM manipulation is performed on the Clone node
ul.parentNode.replaceChild(cloneUl, ul)
Copy the code

Cache layout information

As you already know, retrieving page layout information causes the browser to force a refresh of the render queue. It is therefore necessary to reduce these operations. Developers can cache the first page information into local variables and then manipulate local variables, as shown in the following pseudo-code example:

/ / and inefficient
element.style.left = 1 + element.offsetLeft + 'px';
element.style.top = 1 + element.offsetTop + 'px';
if (element.offsetTop > 500) {
    stopAnimation();
}
/ / and efficient
var offsetLeft = element.offsetLeft;
var offsetTop = element.offsetTop;
offsetLeft++;
offsetTop++;
element.style.left = offsetLeft + 'px';
element.style.top = offsetTop + 'px';
if (offsetTop > 500) {
    stopAnimation();
}
Copy the code

conclusion

To reduce the performance cost of redrawing and rearranging, you can improve your Web application by:

  1. Batch changes to DOM and styles
  2. “Offline” manipulation of the DOM tree, out of the document flow
  3. Caching to local variables reduces the number of times page layout information is accessed

reference

High-performance JavaScript