Script tag in an HTML document
This post will introduce the specs of a script tag's properties, which might help if you want to optimize your page load time.
Script tag loading order
The following image explains the loading time of a script tag based on its property. There are several notes:
defer
should not be combined withasync
. By the spec's definition,async
has the higher priority, which meansdefer
is ignored.
For classic scripts, if theasync
attribute is present, then the classic script will be fetched in parallel to parsing and evaluated as soon as it is available (potentially before parsing completes). If theasync
attribute is not present but thedefer
attribute is present, then the classic script will be fetched in parallel and evaluated when the page has finished parsing. If neither attribute is present, then the script is fetched and evaluated immediately, blocking parsing until these are both complete.
- If there are several script tags,
defer
scripts are executed in the order they appear in the document, whileasync
scripts are executed asynchronously whenever they are finished parsing. That is to say, theasync
-type scripts execution order is not preserved.
For classic scripts/module scripts, if the async
attribute is present, then the classic/module script will be fetched in parallel to parsing and evaluated as soon as it is available (potentially before parsing completes)
- Notice the red color. When a script is executed, the document stops parsing because the script can contain a DOM modification such as
document.write()
. module
-type scripts aredefer
by default. Indeed, if thedefer
attribute is provided on amodule
-type script, it has no effect. Whenasync
is provided to amodule
-type script, it becomes async.
From WHATWG specs:
The defer
attribute has no effect on module scripts.
- With inline scripts (i.e
src
is not provided), formodule
type scriptsasync
can be used, while for classic scripts bothasync
anddefer
cannot be used.
Classic scripts may specifydefer
orasync
, but must not specify either unless thesrc
attribute is present. Module scripts may specify theasync
attribute, but must not specify thedefer
attribute.
Is the inline content of a script with src executed?
In other words, if there is a script tag <script src='./app.js'>window.start()</script>
, what will happen? (earlier spoiled answer: NO. And this is never recommended).
In the HTML 4.01 specification of w3c, if both src
and inline content are set, the script retrieved via src
is executed, and the inline content is ignored.
The script may be defined within the contents of the SCRIPT element or in an external file. If the src attribute is not set, user agents must interpret the contents of the element as the script. If the src has a URI value, user agents must ignore the element's contents and retrieve the script via the URI
From the latest HTML (5) specification from WHATWG (Last Updated 22 April 2021), when src
is provided, the content must be empty. Otherwise, the browser can throw errors, leading to unexpected behavior. However, in practice, the browsers just ignore the inline content.
If there is nosrc
attribute, depends on the value of thetype
attribute, but must match script content restrictions.If there is asrc
attribute, the element must be either empty or contain only script documentation that also matches script content restrictions.
While it is not recommended, several libraries might use the inline content of the script tag to provide text information to the script. In that case, if the script is loaded as a module, use import.meta
.
currentScript
, otherwise, use Document.currentScript
to return the <script>
element whose script is currently being processed.
Note that: these APIs are only able to reference the <script>
element if they are called synchronously from the top level of the script (codepen).
How to execute an inline code after a remote script is loaded?
The solution: dynamically load the script and listen for its load
event to execute the inline code.
const script = document.createElement('script')
script.src = 'https://code.jquery.com/jquery-3.6.0.min.js'
script.async = true
script.addEventListener('load', () => {
script.parentNode?.removeChild(script)
// now you can use jquery
// inline code here
})
document.head.appendChild(script)
How to load a style sheet asynchronously?
At the end of your document, put the following html value:
Full list of a script tag's attributes
From WHATWG specs:
src
: string.type
: string.nomodule
: boolean.async
: boolean.defer
: boolean.crossorigin
: string.integrity
: string.referrerpolicy
: string.
And global attributes which can be applied to any HTML element.