Frontend

Discover Resources How To Block Rendering

External resources commonly found on web pages include scripts, stylesheets, fonts, images, and videos. It is important to understand how these external resources impact the rendering of the entire page.

Prepare

Before conducting the test, it is necessary to control the download speed of the browser and reset it to 50kb/s. The procedure for doing so is as follows:

1. Open the Chrome Developer Tools.

2. Navigate to the Network panel. Select “Add” to add a custom throttling configuration.

3. Create a configuration with a download speed of 50kb/s.

4. Finally, select the newly created configuration from the dropdown menu in step 2.

5. Note: If the currently selected custom option is modified, it is necessary to switch to a different option and then switch back for the changes to take effect.

Images

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            console.log('DOMContentLoaded')
        })
        window.onload = function() {
            console.log('onload')
        }
    </script>
</head>
<body>
    <h1>h1</h1>
    <img src="https://xxx.oss-cn-shenzhen.aliyuncs.com/images/flow.png" />
    <h2>h2</h2>
</body>
</html>

The size of the image above is approximately 200kb. When the network download speed is limited to 50kb/s and the webpage is opened, the following results can be observed: when the h1 and h2 tags are rendered and the DOMContentLoaded event is triggered, the image is still loading. This indicates that the image does not block the loading of the DOM, nor does it block the loading and rendering of the page. When the image finishes loading, the load event is triggered, indicating a delay in the triggering of the load event.

Similarly, videos, fonts, and images do not block the loading and rendering of the DOM.

CSS styles

The code below loads the bootstrap.css file, which has a size of 192kb. Therefore, in theory, it should take approximately 3 to 4 seconds to download.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" />
</head>
<body>
    <h1>This is h1</h1>
</body>
</html>

The testing process is as follows:

1. In the Elements panel, the h1 tag is selected and removed from the DOM.

2. The browser is refreshed, and immediately after, the h1 tag is loaded under the Elements panel. After an additional 3 to 4 seconds of loading (during which the bootstrap.css is being loaded), the page displays the text “This is h1”. At this point, the page has finished rendering.

Conclusion:

1. The presence of the h1 tag in the DOM before the completion of the loading of bootstrap.css indicates that CSS does not block the parsing of the DOM.

2. The appearance of the text within the h1 tag only after the completion of the loading of bootstrap.css suggests that CSS does block the rendering of the DOM.

Process of Page Rendering

1. The process of parsing the Document Object Model (DOM) and parsing CSS occurs concurrently, aligning with the first conclusion.

2. Rendering of the webpage necessarily takes place after obtaining the CSS Object Model (CSSOM) tree, aligning with the second conclusion.

Does CSS always block DOM rendering?

The answer is no. When you place external stylesheets at the end of the <body> for loading.

<body>
    <h1>This is h1</h1>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" />
</body>

At this point, refreshing the browser will immediately display the text “This is h1” on the page. However, when the style finishes loading after 3 to 4 seconds, it causes a second rendering, resulting in the text being rendered again. This indicates that CSS blocking delays the rendering of the DOM, only blocking the DOM elements defined after the CSS. The second rendering can lead to a poor user experience and increase the burden on the browser. Therefore, this is why it is necessary to load external stylesheets in the section.

Does CSS block the execution of JavaScript that comes after it?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" />
</head>
<body>
    <h1>This is h1</h1>
    <script>
        console.log('888')
    </script>
</body>
</html>

When refreshing the browser, it can be observed that there is no printed content in the browser Console panel. However, when the styles are loaded, the number 888 is printed. This indicates that CSS blocks the execution of JavaScript defined after it.

The reason for this is as follows: if the operations executed in JavaScript require accessing the current style of the h1 tag, but the style has not finished loading, the desired result cannot be obtained. This demonstrates that CSS needs to block the execution of JavaScript defined after it.

Scripts

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <script async src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
    <h1>This is h1</h1>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" />
</body>
</html>

Firstly, the existing h1 tag in the webpage should be removed, if it exists. The Elements panel should be carefully observed. Upon refreshing the browser, if the h1 tag is not loaded (resulting in a blank screen), it is only after the completion of JS loading that the h1 tag appears in the DOM. This clearly indicates that the loading and execution of scripts block the parsing and rendering of the DOM and CSS defined after it. Therefore, it is advisable to place <script > at the end of the tag to minimize the white screen loading time of the webpage.

Why does this happen? Consider the scenario where DOM and CSS are being parsed and rendered, and at the same time, the operations executed in JS modify the style or content of the current h1 tag, which leads to conflicts.

defer & async

defer

1. The browser will continue parsing the HTML and concurrently download scripts. However, the execution of defer scripts will only begin after the completion of DOM construction, ensuring that it does not cause any blocking.

2. Once a defer script has finished downloading, it will always be executed before the DOMContentLoaded event is triggered.

3. The execution order of multiple defer scripts strictly follows the order in which they are defined.

4. The “defer” attribute does not have any effect on the execution of scripts with the “type” attribute set to “module”.

async

1. Browsers continue parsing HTML while concurrently downloading scripts, and once a async script is downloaded, it is immediately executed.

2. Similar to “defer”, downloading scripts does not cause blocking, but if the script is executed before the DOM is fully parsed, it will block the parsing process.

3. The order of execution for “async” scripts and the triggering of “DOMContentLoaded” cannot be determined, as the script may not have finished downloading when the DOM is constructed, or it may have been downloaded earlier.

4. Multiple “async” scripts are executed based on the principle of the first one to finish downloading is executed first, which can lead to errors when there are sequential dependencies between them.

Do dynamic scripts cause blocking?

The loading of dynamically inserted scripts does not impede page parsing and rendering. Once loaded, the scripts are executed immediately, similar to the behavior of the “async” attribute. To ensure the sequential execution of multiple dynamically inserted scripts, the “script.async” property can be set to false, causing the scripts to be executed in the order of their insertion.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Test</title>
</head>

<body>
  <script>
    function loadScript(src) {
      let script = document.createElement('script');
      script.src = src;
      document.body.insertAdjacentElement('afterbegin', script);
    }
    
    document.addEventListener('DOMContentLoaded', () => {
      console.log('DOMContentLoaded');
    });
    
    loadScript('https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js')
  </script>
  <h1>This is h1</h1>
</body>

</html>

If a dynamically inserted script element does not have an src attribute and its type attribute is not “module”, it will immediately block the parsing and rendering of the page. This behavior is explained in detail in section 4.12.1 The script element of the HTML documentation by whatwg. According to the interpretation of the document, this inline script will be immediately pushed to the top of the execution stack and executed.

// index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Test</title>
    <script defer src="js/test.js"></script>
  </head>
  <body>
    <div id="test">test1</div>

    <script>
      let script = document.createElement("script");
      script.text = "console.log(1)";
      document.body.appendChild(script);
      console.log(2);

      window.onload = function() {
        console.log("window load...");
        console.log("onload: ", window.React);
      };
    </script>
    <div>test2</div>
  </body>
</html>


// test.js
console.log('domcontentloaded');

// output
1
2
domcontentloaded
window load...

DOMContentLoaded & load

The DOMContentLoaded event fires when the HTML document has been completely parsed, and all deferred scripts (<script defer src="…"> and <script type="module">) have downloaded and executed. It doesn’t wait for other things like images, subframes, and async scripts to finish loading.

DOMContentLoaded does not wait for stylesheets to load, however deferred scripts do wait for stylesheets, and the DOMContentLoaded event is queued after deferred scripts. Also, scripts which aren’t deferred or async (e.g. <script>) will wait for already-parsed stylesheets to load.

A different event, load, should be used only to detect a fully-loaded page. It is a common mistake to use load where DOMContentLoaded would be more appropriate.

The DOMContentLoaded event is not cancelable.

Leave a Reply

Your email address will not be published. Required fields are marked *