Why is DOM slow?

Source: Internet
Author: User
I have always heard that DOM is slow and should be operated as little as possible. So I want to further explore why everyone will say this. I learned some materials online and sorted them out here. First, the DOM object itself is also a js pair...

I have always heard that DOM is slow and should be operated as little as possible. So I want to further explore why everyone will say this. I learned some materials online and sorted them out here.

First of all, the DOM object itself is also a js object, so strictly speaking, it is not slow to operate on this object, but that some browser behaviors will be triggered after the object is operated, such as layout) and painting ). Next, we will introduce these browser behaviors, illustrate how a page is finally presented, and explain some bad practices and some optimization solutions from the code perspective.

How does a Browser display a page?

A browser has many modules, including the rendering engine module for page presentation, WebKit and Gecko, which only involve the content of this module.

First, let's give a rough description of this process:

  • Parse HTML and generateDOM tree

  • Parse various styles and generate a tree with DOM treeRender tree

  • Calculates layout information for each node of the Render tree, such as the position and size of the box.

  • Draw Based on the Render tree and the UI Layer of the browser

The nodes on the DOM tree and Render tree do not have one-to-one correspondence. For example, a display: none node will exist on the DOM tree instead of the Render tree, this node does not need to be drawn.

There are many factors that affect page rendering. For example, the link position may affect the presentation of the first screen. However, here we will focus on layout-related content.

Painting is a time-consuming process. However, layout is a more time-consuming process. We cannot determine whether layout must be implemented from top to bottom or from the bottom up, even a layout operation involves re-calculation of the entire document layout.

HoweverLayout cannot be avoided.So we mainlyMinimize the number of layout times.

When will the browser perform layout?

Before considering how to minimize the number of layout operations, you need to know when the browser will perform layout.

Layout (reflow) is generally called layoutThis operation is used to calculate the position and size of elements in the document and is an important step before rendering. When HTML is loaded for the first time, there will be a layout event. js script execution and style changes will also lead to browser execution of layout, which is also the main content to be discussed in this article.

In general, layout is lazy in the browser. That is to say, DOM will not be updated during Javascript script execution, and any modifications to the DOM will be saved in a queue, after the execution context of the current js is complete, layout is performed based on the modifications in the queue.

However, sometimes the browser has to execute layout in advance to immediately obtain the latest DOM node information in js Code, which is the main cause of DOM performance problems.

The following operations will break the general rule and trigger the browser to execute layout:

  • Get the DOM attribute to be calculated using js

  • Add or delete DOM elements

  • Resize browser window size

  • Change font

  • Css pseudo-class activation, such as hover

  • Modify the DOM element style through js and the style involves changing the size

Here is an intuitive example:

// Readvar h1 = element1.clientHeight;// Write (invalidates layout)element1.style.height = (h1 * 2) + 'px';// Read (triggers layout)var h2 = element2.clientHeight;// Write (invalidates layout)element2.style.height = (h2 * 2) + 'px';// Read (triggers layout)var h3 = element3.clientHeight;// Write (invalidates layout)element3.style.height = (h3 * 2) + 'px';

Here a clientHeight attribute is involved. This attribute needs to be calculated, so a layout of the browser will be triggered. We can use chrome (v47.0)'s developer tool to check that (timeline record in has been filtered and only layout is displayed ):

In the above example, the code first modifies the style of an element, and then reads the clientHeight attribute of another element. Due to the previous modification, the current DOM is marked as dirty, to ensure that this attribute can be accurately obtained, the browser will perform a layout (we find that chrome's developer tool prompts us about this performance problem ).

It is easy to optimize this code. Read the required attributes in advance and modify them together.

// Readvar h1 = element1.clientHeight;  var h2 = element2.clientHeight;  var h3 = element3.clientHeight;// Write (invalidates layout)element1.style.height = (h1 * 2) + 'px';  element2.style.height = (h2 * 2) + 'px';  element3.style.height = (h3 * 2) + 'px';

Let's take a look at this situation:

Next we will introduce some other optimization solutions.

Minimize layout

The above mentioned batch read/write is mainly caused by obtaining an attribute value to be calculated. What values need to be calculated?

This link describes most of the attributes to be calculated :#

Let's take a look at other situations:

A series of DOM operations

For a series of DOM operations (addition, deletion, and modification of DOM elements), the following solutions are available:

  • DocumentFragment

  • Display: none

  • CloneNode

For example (only documentFragment is used as an example ):

var fragment = document.createDocumentFragment();  for (var i=0; i < items.length; i++){    var item = document.createElement("li");  item.appendChild(document.createTextNode("Option " + i);  fragment.appendChild(item);}list.appendChild(fragment);

The core idea of such optimization schemes is the same, that isFirst, perform a series of operations on a node not on the Render tree, and then add the node back to the Render tree. In this way, no matter how complicated DOM operations are, layout will be triggered only once..

Face style Modification

For style changes, we need to know firstNot all style modifications will trigger layout.Because we know that layout is used to calculate the size and size of the RenderObject, I will not trigger layout if I only change the color.

Here is a website CSS triggers, which lists in detail the effect of each CSS attribute on layout and painting in the browser.

In the following case, the optimization part is the same. Pay attention to read and write.

elem.style.height = "100px"; // mark invalidated  elem.style.width = "100px";  elem.style.marginRight = "10px";elem.clientHeight // force layout here

But I want to mention the animation. Here we talk about js animation, for example:

function animate (from, to) {    if (from === to) return  requestAnimationFrame(function () {    from += 5    element1.style.height = from + "px"    animate(from, to)  })}animate(100, 500)

Every frame of an animation will lead to layout, which is unavoidable. However, to reduce the performance loss caused by layout, you can absolutely position the animation elements so that the animation elements are separated from the text stream, layout reduces the amount of computing.

Use requestAnimationFrame

Any operations that may cause re-painting should be placed in requestAnimationFrame

In real projects, code is divided by module, and it is difficult to organize batch read/write as in the previous example. In this case, you can place the write operation in the callback of the requestAnimationFrame, so that the write operation can be executed before the next painting.

// Readvar h1 = element1.clientHeight;// WriterequestAnimationFrame(function() {    element1.style.height = (h1 * 2) + 'px';});// Readvar h2 = element2.clientHeight;// WriterequestAnimationFrame(function() {    element2.style.height = (h2 * 2) + 'px';});

We can clearly observe the trigger time of the Animation Frame. The MDN is triggered before painting, but I guess it is executed before the js script gives control to the browser for DOM invalidated check.

Other notes

In addition to performance issues caused by layout triggering, some other details are listed here:

Cache the results of the selector to reduce DOM queries. HTMLCollection must be added here. HTMLCollection is the object type obtained through document. getElementByTagName, which is similar to the array typeEach time you get an attribute of this object, it is equivalent to a DOM query.:

var ps = document.getElementsByTagName("p");  for (var i = 0; i < ps.length; i++){  //infinite loop    document.body.appendChild(document.createElement("p"));}

For example, the above Code will lead to infinite loops, so the most cache is required for processing HTMLCollection objects.

In addition, reducing the nesting depth of DOM elements and optimizing css, removing useless styles can help reduce the amount of layout computing.

In DOM query, querySelector and querySelectorAll should be the final choice. They are the most powerful, but have poor execution efficiency. If possible, try to use other methods instead.

The following two jsperf links can compare the performance.

#
#

Your own thoughts on the View layer

There are many theoretical aspects of the above content. from a practical perspective, the content discussed above is exactly what needs to be handled at the View layer. There is already a library named FastDOM to do this, but its code is as follows:

fastdom.read(function() {    console.log('read');});fastdom.write(function() {    console.log('write');});

The problem is obvious, it will lead to callback hell, and it can be foreseen that imperative code like FastDOM lacks scalability. The key is that it becomes a problem of asynchronous programming after requestAnimationFrame is used. To synchronize the read/write status, you must write a Wrapper based on the DOM to internally control asynchronous read/write, I think we can consider directly accessing React ......

In short, try to avoid the problems mentioned above, but if you use a library, such as jQuery, the layout problem lies in the abstraction of the library. Like using React to introduce its own component model and using virtual DOM to reduce DOM operations, layout can be used only once each time the state changes. I don't know if requestAnimationFrame is used internally, I feel that it is quite difficult to build a View layer. Then I will prepare to learn the React code. I hope that I will come back to this question in a year or two, so I can have some new insights.

 

The above is why DOM operations are slow. For more information, see the PHP Chinese website (www.php1.cn )!


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.