Author: Anton RaymerIn the
first part of the article, based on my webinar, we looked at the general principles of the browser. In the second - I focused on important events: repaints and reflows - and on the principles of the event loop.
Repaints and reflows
When loading a page, if it is not empty, always executes at least one reflow and repaint. Further, these events occur in the following cases:
1. A part of the display tree needs recalculation, i.e., a node has a width, height, or coordinates. The reflow event is triggered.
')
2. As a result of the changes, a part of the displayed content should be updated. It is, first of all, about properties of styles: background color, radius, etc. The repaint event is triggered.
If reflow is called, after it the repaint is sure to be called. But the opposite is not true: repaint can be called independently of the reflow.
What actions cause reflow and / or repaint
1) Adding, updating, deleting a DOM node. Because at these events it is necessary to recalculate the display tree.
Functions:
insertAdjacentHTML (), appendChild (), insertBefore (), removeChild (), replaceChild (), remove (), append () / prepend (), after () / before (), replaceWith ()
Modifying DOM node properties:
innerHTML, innerText, width, height, offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop / Left / Width / Height, clientTop / Left / Width / Height
Querying DOM node properties (without modifying it):
(offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop / Left / Width / Height, clientTop / Left / Width / Height
Not only a function call and a change in DOM nodes can cause a reflow, but also a simple request for some properties, which, in particular, include all offset properties. Why this requires layout, I will tell later.
2) Hiding the DOM node using
display: none (reflow and repaint) or
visibility: hidden (only repaint, because there are no geometric changes).
3) Move, animate the DOM node.The coordinates of the animated node of the display tree change, it can also cause resizing of other nodes.
4) Adding / modifying CSSleft, top, right, bottom, width, height
If we want to change these css-properties, this will also cause a reflow.
5) Custom actions: resize the window (resize), change the font, scroll (scroll), drag and drop.6) Other- JS Scrolling : Scrolling through the script and its corresponding properties.
scrollByLines (), scrollByPages (), scrollHeight, scrollIntoView (), scrollIntoViewIfNeeded (), scrollLeft, scrollTop, scrollWidth
- Global methods and events for the window object:getComputedStyle (), scrollBy (), scrollTo (), scrollX, scrollY
- Work with SVGExample
var bstyle = document.body.style; // cache bstyle.padding = "20px"; // reflow, repaint bstyle.border = "10px solid red"; // another reflow and a repaint bstyle.color = "blue"; // repaint only, no dimensions changed bstyle.backgroundColor = "#fad"; // repaint bstyle.fontSize = "2em"; // reflow, repaint // new DOM element - reflow, repaint document.body.appendChild(document.createTextNode('dude!'));
On the example of this script, we see that, if we change the padding, we have to recalculate the size of the rectangle. Accordingly, reflow and repaint will be called. If we change the border, it will be given a new width, so reflow and repaint will also be called. And if we just changed the color of the border, background, or text, only repaint would have caused it. Change the font size - reflow and repaint.
The browser compiles together several requests for reflow and repaint, and performs them once, optimizing its work. But some actions force the browser to execute these events immediately.
Properties:
1. offsetTop, offsetLeft, offsetWidth, offsetHeight
2. scrollTop / Left / Width / Height
3. clientTop / Left / Width / Height
4. getComputedStyle (), or currentStyle in IE
When requesting these properties, the browser needs to be repacked immediately, since they must return relevant information. Therefore, whenever we request these properties, reflow and repaint occur.
Code Optimization Tips for Repaint and Reflow Accounting
I. Do not change the element styles directly in the code, use css-classes to switch the appearance of the element. Or use the cssText property. // bad var left = 10, top = 10; el.style.left = left + "px"; el.style.top = top + "px"; // better el.className += " theclassname"; // or when top and left are calculated dynamically... // better el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
Ways:
1. Switch the CSS class. Often, the appearance of a block or its state can be changed using a class. This option is better than working directly with styles, because at least it will still cause reflow and repaint, but only once. For example, in this case, in the first example, reflow and repaint will occur when the left event changes, and another reflow and repaint will occur when the top event changes. But if we left and top were registered in the css-property in some class, then when adding this class, we would have one reflow and repaint instead of two.
2. The same goes for the cssText property. If we replace all the properties we need with dynamic ones in this text, then we will have one reflow and repaint.
Ii. Combine manipulations with the DOM and perform them separately from the DOM update:This can be done in several ways:
• Use documentFragment, which is designed to optimize the work with DOM-nodes.
• Create a clone of the DOM node, work with it, and when finished, replace the original DOM node with it. It also optimizes the amount of reflow and repaint.
• Use display: none (1 reflow, repaint), make 100 changes, restore the display (1 reflow, repaint). This is better than calling one reflow on every change out of 100.
Iii. Do not query calculated styles too often. This forces the browser to reflow and repaint. We give an example of how to optimize the code in this case. If you need to work with them, take them once, cache it in a variable (if there is any particular cycle, it’s better to work with the variable than to take properties each time) and work with it.
// no-no! for(big; loop; here) { el.style.left = el.offsetLeft + 10 + "px"; el.style.top = el.offsetTop + 10 + "px"; } // better var left = el.offsetLeft, top = el.offsetTop esty = el.style; for(big; loop; here) { left += 10; top += 10; esty.left = left + "px"; esty.top = top + "px"; }
Iv. Think about the display tree and try to understand how many changes you are causing in it.At this point I will focus in more detail, because the layout of the layout is different. And one layout (reflow - if anyone has forgotten) can be much more labor-intensive than the other. Consider this example.
<div class="five red"> <div class="four yellow"> <div class="three green"> <div class="two blue"> <div class="one purple"> </div> </div> </div> </div> </div>

Here is a small fragment of html. Suppose we want to insert some DOM node in block 1.

Where it leads? First, block 1 will have to recalculate its size, because it is not known what size a new inserted DOM node will be. It is necessary to understand whether there is a need to make the width larger or smaller, etc. Changing the size of block 1 causes the block 2 to be recompiled. This causes the block 3, 4 and 5 to be recomposed. That is, we see that the operation is labor-intensive. But everything will be different if we insert our DOM node, for example, after the fourth DOM node.

In this case, only the fifth DOM node will be updated, and the layout is thus much faster.
Another good example is working with animation. Suppose we want to animate the movement of a block of 1 to 200 pixels to the right. If block 1 has position: static, that is, it is in the standard layout stream and is taken into account by other blocks, then changing its coordinates will re-arrange all of its ancestors. If block 1 has position: absolute, it will exit the standard layout stream. In this case, a change in its coordinates will only require rebuilding its individual ancestors. Suppose that rectangle 5 has position: relative, and our rectangle 1 will be linked relative to block number 5. Then the change in block 1 of its coordinates will cause the layout of only the fifth DOM node.
Event loop
Consider the topic of asynchronous events and the Event loop, because, in my opinion, it often causes confusion.

"
What is an event loop? This is an infinite loop that is implemented by the JS engine (V8, JavaScriptCore, etc.) and is designed to correctly execute the script, in particular, asynchronous events. JS engine should not be confused with browser engines, which include Webkit and Gecko. Also, do not confuse the event loop and the browser loop.
On the client side, there are three types of asynchronous actions:
1. Timer.
setTimeout(function timerFn(){ console.log('timerFn'); },100)
2. Ajax
$.ajax({ url: 'someUrl', success: function ajaxFn(data) { console.log('ajaxFn') } });
3. Custom action.
$('something').on('click', function clickFn(){ console.log('clickFn') } )
All of these asynchronous events deal with callbacks. The callbacks mechanism ensures that asynchronous events work properly. Let's look at a small piece of code. We have a callback clickFn (). It runs when we click on the item something. Suppose at this time we have some very large code in the call stack. clickFn will fall into the callbacks queue and will wait there for its turn.
Suppose the following server response is returned from the server - the corresponding callback ajaxFn rises in turn. After that, we have a timer, and its callback also falls into place in this queue. By the way, the developer needs to understand that the time specified by him as a delay for the timer is an approximate number of milliseconds. Because the browser will need to take into account all callbacks that are already queued in callbacks. If, say, the callback clickFn and ajaxFn are executed within a millisecond each, the callback timer will be executed not after 100 milliseconds, as stated in the script, but after 102. However, in a normal situation such errors can be neglected.
So, we have a lineup:

When our Call Stack code has stopped executing, the browser can execute the first callback code from the callbacks queue. Actually, clickFn is executed. When it is executed, the place is freed. It is followed by the next callback - ajaxFn, then - timerFn, and the queue is empty. This is a simplified work loop event loop.
The callbacks queue allows them to be executed not in parallel, but sequentially, one after another. Since in the callback, we can work with the same variables, with the same DOM nodes, their parallel, i.e., asynchronous execution will cause conflicts. In this case, the browser will not be able to figure out what exactly needs to be done.
The next interesting point is that JavaScript is not a blocking language, despite allowing asynchronous events. If we are running AJAX, the code following it will still be executed - it does not wait for a response from the server. Of course, there may be exceptions in the form of alert events or synchronous AJAX, but, as you know, this is not the best practice.
The same can be said about streams. JavaScript runs in a single thread that works with the DOM. By and large, there is no point in creating several parallel threads - their presence creates additional confusion and makes it difficult to work with the DOM. But the way to organize parallel streams for the script does exist: use Web Worker - a technology that appeared in the html5 standard. It has one limitation - Web Worker cannot work directly with the DOM and must interact with the main thread by exchanging messages.
Sources:1.
http://www.html5rocks.com/ru/tutorials/internals/howbrowserswork/2.
http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/3.
http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html