HTML Script Element Attributes: async vs. defer vs. type=’module’

How do script elements affect page loading?

Zachary Lee
Level Up Coding

--

Photo by Mike van den Bos on Unsplash

I believe you are no stranger to script elements in HTML. It has some properties such as async, defer, etc. Do you know what they do and the difference between them?

If the script element does not have any attributes added, then when the browser encounters the script, it will be loaded and executed immediately. After waiting for the execution to complete, it will continue to parse the following tags. This stage is called Parse HTML.

And if the script is large or time-consuming to execute, the page will be blank for a long time. Just like the screenshot above. This is especially common in some SPA front-end projects.

So for this situation, we can add attributes to script elements to optimize. Next, I divide script elements into three categories to explain separately:

Classic scripts

When we omit the type attribute for script elements or configure as JavaScript MIME type (the only valid value currently text/javascript), the script is a classic script.

<script type="text/javascript" async></script>

When the async attribute is configured for a classic script, the script is fetched immediately but does not block parsing HTML. Once the fetch is complete, it will be executed immediately, and if the HTML is not parsed at this time, it will be blocked.

<script type="text/javascript" src="/test.js" defer></script>

When the defer attribute is configured for a classic script, the script is fetched immediately but does not block parsing HTML. It is not executed until the HTML is parsed, but it blocks the DOMContentLoaded event.

It should be noted that if this classic script does not use the src attribute, that is, an inline script, the defer attribute will have no effect.

Module scripts

When we configure type="module" for a script element, then this script is a module script.

<script type="module"></script>

ESM can be used inside a module script, and even if the same module script is loaded multiple times, the script will only be executed once.

In the loading mechanism, its effect is the same as the defer of the classic script.

It is worth noting that if the module script is configured with the src attribute, then unlike the classic script (which allows cross-domain), this module script needs to use the CORS protocol for cross-domain fetching.

<script type="module" async></script>

When the async attribute is configured for a module script, its loading mechanism is the same as the classic script configured with async.

<script type="module" defer></script>

When the defer attribute is configured for a module script, this attribute has no effect. Because module scripts are “defer” by default.

Other

When the type attribute value of the script element is not the above two valid values. Then the content in this script element (including src) will be discarded by the browser and will not be downloaded and executed.

Conclusion

The screenshot below clearly shows the difference between these types:

Image from WHATWG

That’s all for today. I’m Zachary and I’ll keep outputting stories related to web development. If you like such stories and want to support me, please consider becoming a Medium member. It costs $5 per month and gives you unlimited access to Medium content. I’ll get a little commission if you sign up via my link.

Your support is very important to me — thank you.

--

--