How do HTML, CSS, and JavaScript become pages?

Usually, we write HTML, CSS, JavaScript, etc., and the browser will display beautiful pages (as shown below), but do you know how they are turned into pages? The principle behind this, estimate a lot of people can not answer.

As you can see, the input on the left side is HTML, CSS, JavaScript data, which is processed by the intermediate rendering module and finally output as pixels on the screen.

1. Rendering process

The browser rendering process is a complex set of mechanisms, so let me show you a few sequence of processes.

The first:

1. HTML loading

HTML is the basis of a web page, which is parsed after downloading

2. Load other static resources

When parsing THE HTML and finding links to other external resources such as CSS, JS, images, etc., it will immediately start another thread to download.

However, when the external resource is JS, the parsing of HTML will stop, and the parsing of HTML will continue after the completion of JS download and execution, preventing JS from modifying the parsing result that has been completed

3. DOM tree construction

At the same time as the HTML is parsed, the parser converts the parsed results into DOM objects to further build the DOM tree

4. CSSOM tree construction

After the CSS is downloaded, the CSS is parsed into CSS objects, and then the CSS objects are assembled to build the CSSOM tree

5. Render tree construction

When both the DOM and CSSOM trees are built, the browser builds a render tree from the two trees

6. Layout calculation

After the render tree is built, the browser calculates the size and absolute position of all elements

7, rendering

After the layout calculation is complete, the browser renders the elements on the page. After being processed by the rendering engine, the entire page is displayed

The second (from browser principles and Practices, more detailed)

According to the chronological order of rendering, the pipeline can be divided into the following sub-stages: DOM tree building, style calculation, layout stage, layering, drawing, partitioning, rasterization and composition

1

1. The renderer transforms the HTML content into a DOM tree structure that can be read.

2. The rendering engine translates CSS styleSheets into styleSheets that browsers can understand, calculating the style of DOM nodes.

3. Create a layout tree and calculate the layout information of the elements.

4. Layer the layout tree and generate a layered tree.

Generate a draw list for each layer and submit it to the composition thread.

6. Composition threads divide layers into blocks and convert blocks into bitmaps in the rasterized thread pool.

7. The composite thread sends the DrawQuad command to the browser process.

8. The browser process generates the page according to the DrawQuad message and displays it on the monitor.

The second process is introduced in detail in this paper

Build a DOM tree

Why build a DOM tree? This is because HTML cannot be understood and used directly by browsers, so you need to transform the HTML into a structure that browsers can understand — a DOM tree.

Take a look at the tree structure:

As can be seen from the figure, the structure of tree is very similar to the “tree” in our real life, in which each point is called node, and the connected nodes are called parent-child nodes. Trees are often used in browsers, for example in the rendering process we will describe below.

Let’s take a look at the DOM tree construction process. You can refer to the following figure:

As can be seen from the figure, the input content to build a DOM tree is a very simple HTML file, which is parsed by AN HTML parser and finally outputs the DOM with tree structure.

To get a more intuitive view of the DOM tree, you can open Chrome’s Developer Tools, select the “Console” TAB to open the Console, type “Document” in the Console, and press Enter. You should see a complete DOM tree structure, as shown below:

As you can see, DOM is almost identical to HTML content, but unlike HTML, DOM is an in-memory tree structure that can be queried or modified using JavaScript.

To see how you can modify the CONTENT of the DOM using JavaScript, type in the console:

`document.getElementsByTagName("p")[0].innerText = "black"`
Copy the code

This line of code changes the content of the first tag to black. You can see the following figure:

As you can see from the figure, a section of modification is performed on the first one

After the JavaScript code of the tag, the content of the first P node of the DOM is successfully modified, along with the content of the page.

Ok, so now that we have generated the DOM tree, we still don’t know the style of the DOM node. To get the DOM node to have the correct style, we need to do style calculation.

2. Recalculate Style

The purpose of style calculation is to calculate the specific style of each element in the DOM node, which can be roughly divided into three steps.

1. Convert CSS to a structure that browsers can understand (CSSOM)

Like HTML files, browsers cannot directly understand the CSS styles of plain text, so when the rendering engine receives CSS text, it performs a conversion operation that converts the CSS text into styleSheets(or CSSOM) that browsers can understand.

As you can see from the figure, there are three main sources of CSS styles:

1. External CSS files referenced by link

2. CSS inside the tag

3, element style property embedded CSS

To understand, you can look at the structure in the Chrome console. Just type Document. styleSheets in the console and you’ll see the structure like this:

As you can see from the figure, the stylesheet contains many styles, including styles from all three sources. Of course, the structure of the stylesheet is not the focus of our discussion today, but you need to know that the rendering engine converts all the CSS text retrieved into styleSheets, and that this structure has both query and modification capabilities, which will provide the basis for future stylesheet operations.

2. Transform property values in the stylesheet to standardize them

Now that we’ve converted the existing CSS text into a structure that browsers can understand, it’s time to standardize the property values.

To understand what attribute value normalization is, you can look at CSS text like this:

body { font-size: 2em } p {color:blue; } span {display: none} div {font-weight: bold} div p {color:green; } div {color:red; }Copy the code

As you can see in the CSS text above, there are many attribute values, such as 2em, Blue and bold. These values are not easily understood by the rendering engine, so you need to convert all values into standardized computed values that the rendering engine can easily understand. This process is called attribute value standardization.

So what are the normalized attribute values?

As you can see, 2em is resolved to 32px, red is resolved to RGB (255,0,0), bold is resolved to 700…

3. Calculate the specific style of each node in the DOM tree

Now that the style properties have been standardized, you need to calculate the style properties for each node in the DOM tree. How? This is where CSS inheritance and cascading rules come in.

First, CSS inheritance. CSS inheritance is when each DOM node contains the style of its parent node.

The second rule in style calculation is style layering. Cascading is a fundamental feature of CSS. It is an algorithm that defines how to combine property values from multiple sources. It is at the heart of CSS, which is highlighted by its full name, cascading style sheets.

In short, the purpose of the style calculation stage is to calculate the specific style of each element in the DOM node. In the calculation process, two rules of CSS inheritance and cascading need to be observed. The final output of this phase is the style of each DOM node, stored in the ComputedStyle structure.

If you want to see how each DOM element ends up Computed, open Chrome’s Developer Tools, select the first “Element” TAB, and then the “Computed” child TAB, as shown below:

The style in which the DOM element is ultimately evaluated

The value of the ComputedStyle for the html.body.div.p tag is shown in the red box above. To see which element you want to view, click the corresponding TAB on the left.

3. Layout stage

Now, we have the DOM tree and the styles of the elements in the DOM tree, but that’s not enough to display the page because we don’t yet know the geometry of the DOM elements. The next step is to figure out the geometry of the visible elements in the DOM tree, a process we call layout.

Chrome performs two tasks in the layout phase: creating a layout tree and calculating the layout.

1. Create a layout tree

You may have noticed that the DOM tree also contains many invisible elements, such as the head tag and elements that use the display: None attribute. So, before displaying, we also build an additional layout tree with only visible elements.

Let’s look at the layout tree construction process with the following figure:

Schematic diagram of layout tree construction process

As you can see from the figure above, all invisible nodes in the DOM tree are not included in the layout tree.

To build the layout tree, the browser basically does the following:

  1. Iterate through all visible nodes in the DOM tree and add these nodes to the layout tree;
  2. Invisible nodes are ignored by the layout tree, such as everything under the head tag, or the body.p.pan element, which is not included in the layout tree because its attribute contains dispaly: None.

Render tree:

You know DOM nodes,

Actually, DOM nodes can be divided into visual nodes and non-visual nodes.

Structured tag nodes such as div and P can be called visual nodes,

Scripts, meta and other nodes that cannot be displayed on the page are called non-visual nodes.

What is a render tree?

How does the browser render the UI?

The browser takes the HTML file and parses it to form a DOM Tree

At the same time, perform CSS parsing to generate Style Rules

Then combine DOM Tree and Style Rules into Render Tree

Elements are laid out in the page and then drawn

Render tree is a tree based on the combination of visual nodes and CSS style sheets;

Note that the PS: display: None element appears in the DOM tree, but not in the render tree;

2. Layout calculation

Now we have a complete layout tree. The next step is to calculate the coordinate positions of the nodes in the layout tree. The layout calculation process is very complicated, so we’ll skip it here and cover it in detail in a later chapter.

When a layout operation is performed, the result of the layout operation is written back into the layout tree, so that the layout tree is both the input and the output. This is an unreasonable place in the layout phase, where the input and output are not clearly distinguished. To address this problem, the Chrome team is refactoring the layout code. The next generation of layout system, called LayoutNG, attempts to separate input and output more clearly, thus making the newly designed layout algorithm simpler.

Current rendering pipeline diagram:

A quick summary:

After the HTML page content is submitted to the rendering engine, the rendering engine first parses the HTML into a DOM that the browser can understand. Then according to the CSS style sheet, calculate the style of all nodes in the DOM tree; It then calculates the geometric position of each element and stores this information in the layout tree.

4, layering,

Now that we have the layout tree and the exact location of each element calculated, is it time to start drawing the page?

Again, the answer is no.

Because there are many complex effects on the page, such as complex 3D transformations, scrolling, or z-indexing, the rendering engine will need to generate a layer for a specific node and a corresponding layer (LayerTree) to make it easier to achieve these effects. If you are familiar with Photoshop, you will easily understand the concept of layers, which are added together to form the final page image.

To get a sense of what Layers are, open Chrome’s Developer Tools and select the Layers TAB to visualize the Layers on your page, as shown in the following image:

The rendering engine gives a page multiple layers of diagrams

As you can see from the image above, the rendering engine assigns many layers to the page, and these layers are stacked together in a certain order to form the final page. You can refer to the image below:

The final display page for layer stacking

Now you know that the browser page is actually divided into layers, which are superimposed to create the final page. Let’s look at the relationship between these layers and the nodes in the layout tree, as shown in the figure below:

In general, not every node in the layout tree contains a layer, and if a node has no corresponding layer, then the node is subordinate to the layer of the parent node. If the SPAN tags in the image above do not have their own layer, they are subordinate to their parent layer. Eventually, however, each node is directly or indirectly subordinate to a layer.

So what criteria does the rendering engine need to meet to create a new layer for a particular node? Generally, elements that satisfy either of the following two points can be promoted to a separate layer.

1. First, elements with cascading context attributes, such as z-index attributes, are promoted to separate layers.

A page is a two-dimensional plane, but a cascading context gives a three-dimensional concept to HTML elements that are distributed along the Z-axis perpendicular to the two-dimensional plane in terms of their attribute priorities. You can use the following image to get a feel for it:

Diagram of cascading context

As you can see from the figure, elements with explicitly positioned attributes, elements with transparent attributes defined, elements with CSS filters, and so on, all have cascading context attributes.

If you want to learn more about cascading context, you can refer to this article

Developer.mozilla.org/zh-CN/docs/…

2. Secondly, places that need to be clipped will also be created as layers.

<style> div { width: 200; height: 200; overflow:auto; background: gray; } </style> <body> <div > <p> So elements that have the property of A cascading context or need to be clipped will be promoted to A separate layer, as you can see below: </p> <p> These layers are organized into a tree structure. </p> <p> Layer trees are created based on the layout tree. To find out which elements need to be in which layers, the rendering engine iterates through the layout tree to create the Update LayerTree. </p> </div> </body>Copy the code

In this case, we limit the size of the div to 200 pixels by 200 pixels. The div contains a lot of text, and the text must be displayed in more than 200 pixels by 200 pixels. At this point, clipping occurs. The following is the run-time result:

When this clipping happens, the rendering engine creates a separate layer for the text section, and if the scroll bar appears, the scroll bar is promoted to a separate layer. You can refer to the following image:

So an element that has the properties of a cascading context or needs to be clipped to satisfy any of these points can be promoted to a single layer.

5. Layer drawing

After building the layer tree, the rendering engine will render each layer in the tree, so how does the rendering engine render layers?

The rendering engine implements layer drawing by breaking the drawing of a layer into a number of small drawing instructions, which are then sequentially assembled into a list of drawings, as shown below:

Draw a list:

As can be seen from the figure, the instructions in the draw list are actually very simple. They are asked to perform a simple drawing operation, such as drawing a pink rectangle or a black line. Drawing an element usually requires several drawing instructions, because each element’s background, foreground, and borders require separate instructions to draw. So in the layer drawing phase, the output is these lists to draw.

1. Generate a drawing list for each layer, which the main thread submits to the composition thread

2. Draw lists are simply lists of draw orders and draw instructions. 2. Draw is actually done by the compositing thread in the render engine.

6. Raster operation

Making lists is just a list of drawing orders and drawing instructions, but drawing is actually done by the compositing thread in the rendering engine. You can see the relationship between the render main thread and the composition thread in the following image:

The composite thread and main thread in the renderer process

As shown above, when the drawing list of layers is ready, the main thread commits the drawing list to the composition thread. How does the composition thread work next?

Composition threads divide layers into blocks and convert blocks into bitmaps in a rasterized thread pool

The compositing thread prioritizes bitmap generation based on the blocks near the viewport, and the actual bitmap generation is performed by rasterization. Rasterization refers to the transformation of a map block into a bitmap. The graph block is the smallest unit for rasterization. The renderer maintains a rasterized thread pool, where all rasterization is performed, as shown below:

The composite thread submits the map block to the rasterized thread pool

Generally, GPU is used to accelerate the generation of bitmaps during rasterization. The process of using GPU to generate bitmaps is called fast rasterization, or GPU rasterization. The generated bitmaps are stored in GPU memory. As you may recall, GPU operations are run in the GPU process, and if rasterization operations use the GPU, then the final bitmap generation is done in the GPU, which involves cross-process operations. You can refer to the picture below for the specific form:

As can be seen from the figure, the rendering process sends the instruction of generating map blocks to GPU, and then executes the bitmap of generating map blocks in GPU, which is saved in GPU memory.

2. Once all the tiles have been rasterized, the compositing thread generates a command to draw the tiles — “DrawQuad” — and submits the command to the browser process.

7. Synthesis and display

Once all the tiles have been rasterized, the composition thread generates a command to draw the tiles — “DrawQuad” — and submits the command to the browser process.

The browser process has a component called viz that receives DrawQuad commands from the compositing thread, draws its page contents into memory, and displays them on the screen.

At this point, through this series of stages, the HTML, CSS, JavaScript, etc., written by the browser will display a beautiful page.

Summary of dyeing line

Ok, we have now analyzed the entire rendering process, from HTML to DOM, style calculation, layout, layering, drawing, rasterization, composition, and display. Here’s a diagram to summarize the entire rendering process:

Complete rendering pipeline diagram

Combined with the above image, a complete rendering process can be summarized as follows:

1. The renderer transforms the HTML content into a DOM tree structure that can be read.

2. The rendering engine translates CSS styleSheets into styleSheets that browsers can understand, calculating the style of DOM nodes.

3. Create a layout tree and calculate the layout information of the elements.

4. Layer the layout tree and generate a layered tree.

Generate a draw list for each layer and submit it to the composition thread.

6. Composition threads divide layers into blocks and convert blocks into bitmaps in the rasterized thread pool.

7. The composite thread sends the DrawQuad command to the browser process.

8. The browser process generates the page according to the DrawQuad message and displays it on the monitor.

2. Update element geometry (rearrangement)

Updates the geometry of the element

As you can see from the above figure, if you modify the geometry of an element using JavaScript or CSS, such as changing the width, height, etc., then the browser will trigger a rearrangement of the layout, followed by a series of sub-stages called rearrangement. Of course, reordering requires updating the entire rendering pipeline, so it’s also the most expensive.

3. Update elements’ draw attributes (redraw)

Next, let’s take a look at how the rendering pipeline works when you change the background color of certain elements using JavaScript. You can refer to the following image:

As can be seen from the figure, if you change the background color of the element, the layout stage will not be performed, because there is no change in the geometry position, so you directly enter the drawing stage, and then perform a series of subsequent stages, this process is called redraw. Redraw eliminates layout and layering, so it is more efficient than rearrange.

4. Direct synthesis stage

So what happens if you change a property that neither layouts nor draws? The rendering engine skips layout and drawing and only performs subsequent compositing operations, which we call compositing. Please refer to the following figure for specific process:

In the image above, we use the TRANSFORM of CSS to animate the effect, which avoids the rearrangement and redraw phases and executes the compositing animation directly on the non-main thread. This is the highest efficiency, because it is composed on the non-main thread, does not occupy the main thread resources, and avoids the layout and drawing two sub-stages, so compared with redraw and rearrangement, composition can greatly improve the drawing efficiency.

How to use these concepts to optimize your page will be explained in more detail in the following sections, but you just need to understand these three concepts and principles in conjunction with the “rendering pipeline”.