A byte interview question refreshed my cognition, and learned new knowledge, happy.
I said at the beginning that the answer is 3 times each, because you get an offsetWidth once, and then you change the style once.
But it turns out that offsetWidth triggers reordering because the render queue is refreshed. In this case, the render queue was empty, so no reordering was triggered. So twice.
Render queue = render queue = render queue = render queue It doesn’t matter if you don’t understand the render queue, look down and you will understand.
There are, of course, antique and modern browsers. Modern browsers (i.e., those with render queues) should do it once, and antique browsers should do it twice
I. What is redrawing and rearrangement
After downloading all the components of the page — HTML tags, JavaScript, CSS, images — the browser parses and generates two internal data structures — the DOM tree and the render tree.
The DOM tree represents the page structure, and the render tree represents how the DOM nodes are displayed. Each node in the DOM tree that needs to be displayed has at least one corresponding node in the render tree (the hidden DOM element disply none has no corresponding node in the render tree). Nodes in the render tree are called “frames” or “boxes,” which conform to the definition of the CSS model, understanding a page element as a box with padding, margins, borders, and positions. Once the DOM and render tree are built, the browser begins to display (draw) page elements.
When a DOM change affects an element’s geometry (width or height), the browser needs to recalculate the element’s geometry, as well as the geometry and position of other elements. The browser invalidates the affected portion of the render tree and reconstructs the render tree. This process is called rearrangement. When the rearrangement is complete, the browser redraws the affected parts to the screen, a process called redraw. Because of the browser’s flow layout, calculations of the render tree are usually done only once. With the exception of table and its inner elements, it can take multiple computations to determine the attributes of its nodes in the rendered tree, often taking three times as long as the equivalent element. This is one reason why we should avoid using tables for layout.
Not all DOM changes affect geometric properties; for example, changing the background color of an element does not affect the width or height of the element; in this case, only redraw will occur.
Whether pages are redrawn or rearranged, they affect performance
How to trigger rearrangement
Changes in page layout and element geometry can cause rearrangements in the following cases
- Initial page rendering
- Add/remove visible DOM elements
- Change element position
- Change element dimensions (width, height, inside and outside margins, borders, etc.)
- Changing element content (text, images, etc.)
- Change window size
The extent and extent of rearrangement will be different under different conditions
In some cases, it even rearranges the entire page, such as sliding the scroll bar
Third, browser optimization
Such as:
Suppose I want to change the style of a div with JS
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '10px';
div.style.height = '10px';
Copy the code
We have modified the elements of the left, top, width and height attributes, meet our rearrangement conditions, four rearrangement will happen in theory, but in fact will only happen once rearrangement, because our modern browsers render queue mechanism, when I change the elements of a style can lead to browser rearrangement or redrawn “, it will enter a render queue, and then the browser will look down, and if there are any style changes below, it will queue again, until there are no style changes below, and the browser will execute the render queue in batches to optimize the rearrangement process, and modify the style as well, so that instead of 4 rearrangements, it will optimize the rearrangement to 1
But, when we write the following code:
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px'; console.log(div.offsetWidth); div.style.height = '20px'; console.log(div.offsetHeight); Copy the code
Is it still a rearrangement?
Obviously not! Four rearrangements have occurred at this point!
The browser has a render queue optimization mechanism. Why four times?
It related to the offsetLeft/Top/Width/Height
OffsetTop, offsetLeft, offsetWidth, offsetHeightClientTop, clientLeft, clientWidth, clientHeightScrollTop, scrollLeft, scrollWidth, scrollHeightGetComputedStyle () (currentStyle in IE)Copy the code
These force a queue refresh and require the style change task to be executed immediately
Because the browser is not sure if the same style will be changed in the following code, it has to perform the render queue trigger reorder immediately to get the correct current time value!!
Redraw and rearrange performance optimization
1. Separate read and write operations
We can optimize the above code
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft); console.log(div.offsetTop); console.log(div.offsetWidth); console.log(div.offsetHeight); Copy the code
That’s only 1 rearrangement!
2. Style set changes
The same code that we originally modified the style
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
Copy the code
Although modern browsers have optimization mechanisms for rendering queues, antique browsers are still inefficient, triggering 4 rearrangements. Even so, we can still make optimization, and we need cssText properties to incorporate all style changes
div.style.cssText = 'left:10px; top:10px; width:20px; height:20px; ';
Copy the code
This requires only one DOM change, only one rearrangement, and only one line of code
In addition to cssText, we can also make style changes by changing the class name
div.className = 'new-class';
Copy the code
This approach is maintainable and helps us avoid explicit code, but it takes a little bit of performance
3. Cache layout information
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
Copy the code
This type of write after the read operation causes two rearrangements
The cache can be optimized
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
Copy the code
This is equivalent to a separate read and write operation, optimized for a single rearrangement
4. Batch operation of elements
Now we want to loop in a lot of Li to ul (if UL doesn’t already exist, the best way is to loop in li to UL and then ul to the document, once again)
var ul = document.getElementById('demo');
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text);
ul.appendChild(li); } Copy the code
I can make the following optimization
var ul = document.getElementById('demo');
ul.style.display = 'none';
for(var i = 0; i < 1e5; i++){
var li = document.createElement('li');
var text = document.createTextNode(i);
li.appendChild(text); ul.appendChild(li); } ul.style.display = 'block'; var ul = document.getElementById('demo'); var frg = document.createDocumentFragment(); for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); frg.appendChild(li); } ul.appendChild(frg); var ul = document.getElementById('demo'); var clone = ul.cloneNode(true); for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); clone.appendChild(li); } ul.parentNode.replaceChild(clone,ul); Copy the code
The principle of reducing redraw and rearrangement in the above method is simple
- Element out of document
- Change the style
- Element regression document
Changing elements uses hidden elements, document fragments, and cloned elements, respectively
This article is formatted using MDNICE