Getting startedChapter 2 of 21
Where To Put It
Script tags, external files, and what defer and async actually change.
Inside the page
JavaScript goes in a <script> element. You can write it directly in the HTML:
HTML
<body>
<h1 id="title">Hello</h1>
<script>
document.getElementById('title').textContent = 'Changed';
</script>
</body>Output
The heading reads "Changed" once the page has loaded.
This is fine for a demonstration. For anything real, put the code in its own file instead.
In a separate file
A separate file can be cached by the browser, reused across pages, and read without wading through markup. Point at it with src:
HTML
<script src="/js/app.js"></script>Output
Loads and runs /js/app.js.
A script element with a src ignores anything written between its tags, so do not try to do both at once.
Where in the document
This matters more than it looks. The browser parses HTML from top to bottom. When it meets an ordinary script it stops, downloads it, runs it, and only then carries on. So a script in the <head> runs before the page below it exists:
JavaScript
// In the head, with the h1 further down the page:
const title = document.getElementById('title');
console.log(title);Output
null
There are two ways out of this, and one of them is much better than the other.
defer and async
Both attributes let the browser keep parsing while the script downloads. They differ in when the script then runs.
| Attribute | Downloads | Runs | Order kept |
|---|---|---|---|
| none | immediately, blocking | as soon as it arrives | yes |
defer | in parallel | after the HTML is parsed | yes |
async | in parallel | the moment it arrives | no |
HTML
<head>
<script src="/js/app.js" defer></script>
</head>Output
Downloads early, runs after the page is parsed, in order.
defer is the sensible default. The script starts downloading straight away, nothing is blocked, the whole document exists by the time it runs, and several deferred scripts still run in the order you wrote them.
Use async only for something genuinely independent, such as an analytics tag that does not care about your other code or about the page.
Tip
You will still see scripts placed at the very bottom of <body>. That was the old fix for the same problem and it works, but defer in the head starts the download sooner and reads more clearly.
Modules
A script marked type="module" can import from other files, and is deferred automatically. Modules get their own chapter later; for now it is enough to know the attribute exists.
HTML
<script type="module" src="/js/main.js"></script>Output
Deferred by default, and can use import and export.
Test yourself
3 questionsWhere does a script with defer run?
Show the answer
After the HTML has been parsed Which is why the whole document is available to it, and why several deferred scripts keep their order.
Why does a plain script in the head often find nothing when it looks for an element?
Show the answer
It runs before the rest of the HTML has been parsed Parsing stops at the script, so anything below it does not exist yet.
When is async the right choice?
Show the answer
For a self-contained script whose order does not matter Async scripts run the moment they arrive, in whatever order they finish, which breaks anything with dependencies.
Output
The four ways to show something, and which one to reach for.