Recently, when the system is combing all the tags involved in HTML5, combing to and

Maybe this way has become a convention, but where is the benefit, or why other ways are not desirable, presumably you and I have the same question, then go back and see.

The preparatory work

The first thing you need to do is set up a server to return CSS styles and JS scripts, and have the server return data with a fixed delay based on the parameters passed.

The directory structure is as follows, where index.js and style. CSS are used to return data, app.js is the server startup file, index.html is the file used to test the case, and the rest of the files or folders can be ignored.

├ ─ ─static│ ├── index.js │ ├─ style.css ├─ index.js │ ├─ index.html ├─ package.json ├─ node_modules/Copy the code

Related to the relevant code also posted it, convenient copy debugging. It is important to note that when running node app.js locally and starting, the browser will access index.html by typing http://127.0.0.1:3000/, And visit the style.css. CSS can enter http://127.0.0.1:3000/static/style.css? Sleep =3000, where the sleep parameter is free to control the CSS file return delay, for example, if you want to return the file after 5s, set sleep=5000.

// app.js
const express = require('express')
const fs = require('fs')
const app = new express()
const port = 3000

const sleepFun = time= > {
    return new Promise(res= > {
        setTimeout(() = > {
            res()
        }, time)
    })
}

const filter = (req, res, next) = > {
    const { sleep } = req.query || 0

    if (sleep) {
        sleepFun(sleep).then(() = > next())
    } else {
        next()
    }
}

app.use(filter)

app.use('/static/', express.static('./static/'))

app.get('/'.function (req, res, next) {
    fs.readFile('./index.html'.'UTF-8'.(err, data) = > {
        if (err) return
        res.send(data)
    })
})

app.listen(port, () = > {
    console.log('App is running at http://127.0.0.1:${port}/ `)})// static/index.js
var p = document.querySelector('p');
console.log(p);

// static/style.css
p { color: lightblue; }
Copy the code

The next step is to prepare the index.html, where the HTML part of the shelf looks like the bottom, and then you just have to remember that the DOMContentLoaded event will be fired after the page’S DOM is parsed.

<! DOCTYPE html><html lang="zh-CN">

<head>
    <script>
        document.addEventListener('DOMContentLoaded'.() = > {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
</head>

<body>
    <p>hello world</p>
</body>

</html>
Copy the code

CSS does not block DOM parsing, but it does block DOM rendering

First, insert the following tag in index. HTML, and then enter http://127.0.0.1:3000/ in your browser to access this page.

<head>
    <script>
        document.addEventListener('DOMContentLoaded'.() = > {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
    <link rel="stylesheet" href="./static/style.css? sleep=3000">
</head>

<body>
    <p>hello world</p>
</body>
Copy the code

The page initially appears blank, the console prints out the P element, and loading loading is loaded on the browser TAB. After 3s, the page displays a light blue Hello World.

In this case, the CSS does not block DOM parsing. If the CSS blocks DOM parsing, the P tag will not be parsed, the DOM will not be parsed, and the CSS request will not trigger the DOMContentLoaded event. Also, during the CSS request, the console prints out the P element immediately, so the CSS does not block DOM parsing.

In another case, although the DOM is parsed very early, the P tag is not rendered. The reason is that the CSS style is not completed. The Hello World is rendered after the style is obtained, so the CSS will block the rendering of the page.

Briefly describe the browser’s parsing and rendering process, parsing DOM to generate DOM Tree, parsing CSS to generate CSSOM Tree, the combination of the two to generate render Tree, the browser render to the page according to the rendering Tree. It can be seen that parsing of DOM Tree and PARSING of CSSOM Tree do not affect each other, and they are parallel. Therefore, CSS does not block DOM parsing, but because the generation of render tree is dependent on DOM tree and CSSOM tree, CSS must block DOM rendering.

More specifically, CSS blocks the generation of the Render Tree, which in turn blocks DOM rendering.

JS blocks DOM parsing

In order to avoid the disturbance caused by loading CSS, the following only focuses on the JS execution, where the logic in the body of the for loop is not considered for the moment, and the JS execution is only allowed to take more time.

<head>
    <script>
        document.addEventListener('DOMContentLoaded'.() = > {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
</head>

<body>
    <script>
        const p = document.querySelector('p')
        console.log(p)
    
        for (var i = 0, arr = []; i < 100000000; i++) {
            arr.push(i)
        }
    </script>
    <p>hello world</p>
</body>
Copy the code

When the browser accesses the page, it is initially blank and the console prints null. After a short loading delay, the console prints the P label and the page renders hello World.

The console prints null at the beginning of the execution, because the p tag has not yet been parsed. When the for loop executes, it is clear that the execution time is long. When the p tag is parsed, the console prints the P tag. Meanwhile, the page renders hello World.

A reasonable explanation is that the browser cannot know the specific content of JS. If it parses the DOM first, the browser will be busy in vain if all the DOM is deleted from JS, so it simply stops parsing the DOM and continues parsing until the JS execution is complete.

CSS blocks JS execution

Insert the tag before the JS script inside the page as follows, and the delay is 3s to get the CSS style.

<head>
    <script>
        document.addEventListener('DOMContentLoaded'.() = > {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
    <link rel="stylesheet" href="./static/style.css? sleep=3000">
    <script src="./static/index.js"></script>
</head>

<body>
    <p>hello world</p>
</body>
Copy the code

The initial page is blank. After loading the browser for 3s, the console prints null, followed by a p label, and the page renders a light blue P label.

It looks like CSS is blocking not only DOM parsing, but DOM rendering as well.

But first think about what is blocking DOM parsing. We just proved that CSS does not block DOM parsing, so it could only be JS blocking DOM parsing. But JS has only two lines of code and won’t block for 3s or so. So there is only one possibility that CSS will block JS execution.

So the output is basically parsed, first to the first

The second

In fact, it makes sense to do this, assuming that the content of the JS script is to get the CSS style attributes of the DOM element. If JS wants to get the correct style of the DOM, it must have all the CSS loaded. Otherwise, the style obtained may be wrong or not up-to-date. Therefore, the JS script cannot be executed until the preceding CSS is loaded, and the browser does so regardless of whether the JS script retrives the STYLE of the DOM element.

Going back to the question at the beginning of this article, it generally makes sense to place

Javascript triggers page rendering

The following CSS uses in-page mode. The color names and RGB values are lightGreen (RGB (144,234,144)) and Pink (RGB (255, 192, 203)), respectively.

// index.html
<head>
    <style>
        p {
            color: lightgreen;
        }
    </style>
</head>

<body>
    <p>hello</p>
    <script src="./static/index.js? sleep=2000"></script>
    <p>beautiful</p>
    <style>
        p {
            color: pink;
        }
    </style>
    <script src="./static/index.js? sleep=4000"></script>
    <p>world</p>
    <style>
        p {
            color: lightblue;
        }
    </style>
</body>

// static/index.js
var p = document.querySelector('p');
var style = window.getComputedStyle(p, null);
console.log(style.color);
Copy the code

The page is initially rendered with a light green Hello, followed by a pink Hello beautiful 2s later and the console prints RGB (144,238,144), Then a light blue hello Beautiful World is rendered after 2 seconds and the console prints RGB (255, 192, 203).

The result is that the browser first parses the first

Then the browser makes a JS request, 2 seconds after JS gets finished, immediately runs the console output RGB (144, 238, 144), after JS is finished, the browser continues down to parse the beautiful text to the P tag and the second

When parsing to the second

To answer that question, the browser parses the DOM line by line, but it preloads an external resource with a reference tag (such as a

This is why CSS blocks JS execution. The browser does not know the contents of the script in advance, so it has to render the page once to make sure that the

CSS within the Body

Let’s look at a special case.

<head>
    <script>
        document.addEventListener('DOMContentLoaded'.() = > {
            var p = document.querySelector('p')
            console.log(p)
        })
    </script>
</head>

<body>
    <p>hello</p>
    <link rel="stylesheet" href="./static/style.css? sleep=3000">
    <p>world</p>
</body>
Copy the code

First, the browser parses the

Therefore, the initial page is blank. After loading the browser for 3s, the console prints out the P label and renders the page as a light blue hello World.

But that’s not the case. Let’s take a look at how it looks in Chrome.

Take a look at the performance in Firefox.

Let’s take a look at the performance in the Opera browser.

Performance of Internet Explorer 11 and below.

Performance in the Edge browser.

Well, come on, there are 5 browsers with 3 representations, and none of them are consistent with the preanalysis.

In fact, the reason for this difference was a phenomenon called Flash of Unstyled Content (FOUC).

Because the browser engine’s architecture is inconsistent, the browser engine has a choice when it parses the CSS in the Body.

One option is to suspend subsequent DOM parsing when the CSS is requested and continue parsing after the CSS is loaded. This would cause the CSS to block DOM parsing, delaying the rendering of the page’s DOM parsing and its styles, which is similar to the JS situation.

Another option is that it can continue parsing at the request of the CSS, where the CSS is loaded in parallel with the DOM parsing, and then update the styles after the CSS is loaded. In this case, some DOM styles will immediately jump from the default style to the styled state, resulting in the style flicker.

This summary doesn’t have to be too confusing, just remember that the CSS in the Body can be rendered differently depending on the browser. This phenomenon is commonly referred to as FOUC, and should be avoided in your code.

From what has been discussed above

Taking all the above facts together, we can come to the following conclusion.

  • CSSDoes not blockDOMParse, but blockDOMRendering, a little more rigorous isCSSblocksrender treeAnd then blockDOMThe rendering of
  • JSblocksDOMparsing
  • CSSblocksJSThe implementation of the
  • Browser encounter<script>Tag and nodeferorasyncProperty triggers page rendering
  • BodyThe inner outer chainCSSIn particular, it will formFOUCUse with caution