DEV Community

Cover image for CSS, JavaScript, and blocking web page parsing
Matvey Romanov
Matvey Romanov

Posted on

CSS, JavaScript, and blocking web page parsing

Recently, I came across an article about the problem of loading CSS files, which slows down the processing of page materials. I read that article, trying to learn something new, but it seemed to me that what it said was not quite true. So I did my own research on this topic and experimented with loading CSS and JavaScript.

Can loading CSS resources block page parsing?

First of all, I will say that the question in the title of this section can, without any doubt, be answered positively. Loading CSS files can not only block HTML code parsing, but also prevent JavaScript code from executing.

To begin with, I suggest experimenting. To do this, we will need to configure the browser accordingly. We will download the CSS file from the CDN, so we will limit the speed of working with the network in the Google Chrome browser. To do this , on the developer tools tabPerformance, change the parameter value Networkto Slow 3G. We will explore the next page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            console.log('DOMContentLoaded');
        })
    </script>
    <script>
        console.log('script');
        Promise.resolve(1).then(res => {
            console.log('then');
        });
    </script>
</head>
<body>
    <h1>hello</h1>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

We download the CSS file from the CDN, but since the Internet connection speed is artificially limited, it will take some time to load the styles. As a result, nothing gets to the JavaScript console before the CSS file is loaded, and the page content is not displayed on the screen. What we're seeing indicates that CSS loading is blocking other page content from being loaded and processed.

image Output of data to the JS console

Can loading and executing JS code block page parsing?

Loading and processing JS files, of course, blocks page parsing. However, to fix this problem, you can use attributes and tags when connecting scripts to the page defer async <script>. Now we will study their impact on page loading.

Normal script loading and execution

If the tag <script> does not use async or attributes defer — the page content loading and processing process is performed as shown in the following diagram. Loading JS files and executing the code contained in them blocks HTML parsing.

image Using the <script> tag without the async and defer attributes

Here and further, we will use the following color symbols.

image HTML parsing — HTML parsing; HTML parsing paused-HTML parsing suspended; Script download — Script loading; Script execution — Script execution

Using the <script> tag with the async attribute

When the browser processes a tag <script> with an attribute async, the JavaScript code is loaded asynchronously. The script code is executed immediately after loading. However, JS code execution blocks HTML parsing.

image Using the <script> tag with the async attribute

Using the <script> tag with the defer attribute

If the tag <script> contains an attribute defer — the script code is loaded asynchronously. However, after the code is loaded, it is executed only when the parsing of the HTML code is completed.

image Using the <script> tag with the defer attribute

Experiments

Let's experiment with async the and attributes defer. Let's start with the next page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DomContentLoaded</title>
</head>
<body>
    <script src="http://code.jquery.com/jquery-1.4.4.min.js">
    </script>
    <script src="./index.js"/> // 0
    <script src="./index2.js"/> // 2
    <script >
    console.log('inline');
        Promise.resolve().then(res=>{
            console.log('then');
        })
    </script>
    <div id="hello">hello world</div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            console.log('DOMContentLoaded');
        })
    </script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

This page, in addition to downloading the script jquery-1.4.4.min.js from the CDN, loads a couple of its own scripts - index.js and index2.js. Below is their code.

File index.js:

Promise.resolve().then((res) => {
    console.log('index1');
    return res;
});
Enter fullscreen mode Exit fullscreen mode

File index2.js:

Promise.resolve().then((res) => {
    console.log('index2');
    return res;
});
Enter fullscreen mode Exit fullscreen mode

When this page loads, the JS console gets what is shown below.

image Output of data to the JS console

As a result, we have proof that loading and processing JS files blocks HTML code rendering. Messages output by scripts appear in the console before the message indicating that the DOM content has finished loading.

Now let's take a look at how scripts <script> that use the attribute in their tags behave <async>:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DomContentLoaded</title>
</head>
<body>
    <script async src="http://code.jquery.com/jquery-1.4.4.min.js">
    </script>
    <script src="./index.js"></script> 
    <script src="./index2.js"/></script>
    <script>
    console.log('inline');
        Promise.resolve().then(res=>{
            console.log('then');
        })
    </script>
    <div id="hello">hello world</div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            console.log('DOMContentLoaded');
        })
    </script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Let's look at what is shown in the console.

image Output of data to the JS console

The jQuery library script is loaded asynchronously. What goes to the console is displayed there before it is loaded. If the library script loads too slowly, it won't interfere with parsing the HTML code. The message DOMContentLoaded can be displayed either before or after the async script is loaded and executed. And when the attribute defer is applied, the script will be loaded asynchronously, wait for the document materials to be processed, and then , but before the eventDOMContentLoaded, it will be executed.

Have you ever encountered problems with blocking the processing of web page content?

Top comments (7)

Collapse
 
sandhilt profile image
Bruno Ochotorena

I think that async allow code execute without stop parse of html.
Read here:
"If async is present: The script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)"
source: w3schools.com/tags/att_script_asyn...

Collapse
 
rahulmanek profile image
rahulmanek

Yes i also definitely agree with this.
async executes while HTML loading.
Checked with idevelop.pro/ consoles.

Collapse
 
faheem_khan_dev profile image
Faheem Khan

This is not correct.
If async ia there, script "download" happens asynchronously not the execution.

Collapse
 
sandhilt profile image
Bruno Ochotorena
Collapse
 
sandhilt profile image
Bruno Ochotorena

Look in source:
"If the async attribute is set, the script is downloaded in parallel to parsing the page, and executed as soon as it is available."

Collapse
 
quantuumsnot profile image
quantuumsnot

In simple words we must defer the script(s) instead of async if we want them to be executed properly on already LOADED HTML content

Collapse
 
sandhilt profile image
Bruno Ochotorena

You can use async + DOMContentLoaded event.
TL;DR; DOMContentLoaded is event when all html is parsed and is safety manipulated.

So, script is async downloaded and execute waiting DOM is ready

Example:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Example</title>

    <script async src="./test.js" />
</head>
<body>
    <main>
        <section id="example"></section>
    </main>
    // ...
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
/* test.js */
(() => {
    function run() {
        const example = document.getElementById("example");
        console.log({ example })
        // ...
    }
    window.addEventListener('DOMContentLoaded', run);
})();
Enter fullscreen mode Exit fullscreen mode

More info about event here: developer.mozilla.org/en-US/docs/W...