Chapters

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

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

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:

JavaScriptthe element has not been parsed yet

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.

AttributeDownloadsRunsOrder kept
noneimmediately, blockingas soon as it arrivesyes
deferin parallelafter the HTML is parsedyes
asyncin parallelthe moment it arrivesno
HTML

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

HTML

<script type="module" src="/js/main.js"></script>

Output

Deferred by default, and can use import and export.

Test yourself

3 questions

Where 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.

Next chapter

Output

The four ways to show something, and which one to reach for.