Greetings to you, dear habravchane! Today I would like to highlight the issue of rendering in web development. Of course, many articles have already been written on this topic, but, as it seemed to me, all the information is rather fragmented and fragmentary. At least, in order to collect the whole picture in my head and comprehend it, I had to analyze a lot of information (mostly English-speaking). That is why I decided to formalize my knowledge into an article, and share the result with the Habr community. I think the information will be useful for both novice web developers and more experienced ones to refresh and structure their knowledge.
This direction can and should be optimized at the stage of layout / frontend-development, since, obviously, markup, styles and scripts are directly involved in rendering. To do this, the relevant specialists must know some subtleties.
I note that the article is not aimed at the exact transfer of the mechanics of browsers, but rather at understanding its general principles. Moreover, different browser engines are very different in the algorithms of work, so it is not possible to cover all the nuances within one article.
The processing of the WEB-page browser
To begin, consider the sequence of the browser when displaying the document:
')
- From the HTML document received from the server, a DOM (Document Object Model) is formed.
- Styles are loaded and recognized, CSSOM (CSS Object Model) is formed.
- On the basis of DOM and CSSOM, a rendering tree is formed, or render tree - a set of rendering objects (Webkit uses the term “renderer” or “render object”, and Gecko - “frame”). Render tree duplicates the DOM structure, but invisible elements do not fall here (for example,
<head>
, or elements with the display:none;
style). Also, each line of text is represented in the render tree as a separate renderer. Each rendering object contains a corresponding DOM object (or a block of text), and a style calculated for this object. Simply put, render tree describes the visual representation of the DOM. - For each element of the render tree, the position on the page is calculated - layout occurs. Browsers use the flow method, in which in most cases a single pass is enough to accommodate all the elements (more tables are required for the tables of passes).
- Finally, there is a drawing of all this stuff in the browser - painting.
In the process of user interaction with the page, as well as the execution of scripts, it changes, which requires repeated execution of some of the above operations.
Repaint
In the case of changing the styles of an element that do not affect its size and position on the page (for example,
background-color
,
border-color
,
visibility
), the browser simply draws it again, taking into account the new style - repaint (or restyle) occurs.
Reflow
If the changes affect the content, the structure of the document, the position of the elements - reflow (or relayout) occurs. The reasons for such changes are usually:
- Manipulations with DOM (add, delete, modify, rearrange elements);
- Content change, incl. text in form fields;
- Calculation or change of CSS properties;
- Add, remove style sheets;
- Manipulations with the attribute "
class
"; - Manipulations with the browser window - resizing, scrolling;
- Activate pseudo-classes (for example
:hover
).
Browser optimization
Browsers, if possible, localize repaint and reflow within the elements that have undergone a change. For example, resizing an absolutely or fixedly positioned element will affect only the element itself and its descendants, while changing a statically positioned element will result in reflow of all elements following it.
Another feature is that during the execution of JavaScript, browsers cache changes that are made, and apply them in a single pass upon completion of the code block. For example, during the execution of this code, only one reflow and repaint will occur:
var $body = $('body'); $body.css('padding', '1px');
However, as described above, accessing the properties of the elements will force a reflow. That is, if we add a reference to the property of the element to the above code block, it will cause an extra reflow:
var $body = $('body'); $body.css('padding', '1px'); $body.css('padding');
As a result, we get 2 reflow instead of one. Therefore, references to the properties of elements should be grouped together in one place, if possible, in order to optimize performance (see a
more detailed example on JSBin ).
But, in practice, there are situations when it is impossible to do without forced reflow. Suppose we have a task: we need to apply the same property (take “
margin-left
”) to an element first without animation (set to
100px
), and then to animate by means of transition to
50px
. You can immediately see
this example on JSBin , but I will sign it here.
First, let's get a class with transition:
.has-transition { -webkit-transition: margin-left 1s ease-out; -moz-transition: margin-left 1s ease-out; -o-transition: margin-left 1s ease-out; transition: margin-left 1s ease-out; }
Then, we will try to realize our plans as follows:
var $targetElem = $('#targetElemId');
This solution will not work as expected, since changes are cached and applied only at the end of the code block. We will be rescued by a forced reflow, as a result, the code will take the following form, and will exactly perform the task:
Practical Optimization Tips
On the basis of this article, as well as other articles on Harba, where the issue of client-side optimization is covered, the following tips can be derived, which will be useful in creating an effective frontend:
For a more detailed study of the issue I recommend to read the articles:
I hope every reader has learned something useful from the article. In any case - thank you for your attention!
UPD: Thanks to
SelenIT2 and
piumosso for correct comments on the effectiveness of processing CSS selectors.