HTMLpad

How to Link JavaScript to HTML

By · · 4 min read

Two sheets of paper on a desk, one with markup and one with neat lines of code, linked by a small orange paper clip

To link JavaScript to HTML, add <script src="script.js" defer></script> inside the <head> of your page. The browser downloads the file and runs it once the page has been read. You can also write JavaScript directly between <script> tags in the page, which is how most single-file pages work.

The defer in that line does more work than it looks. This guide explains where scripts go, what defer and async change, how paths work, and how to find out why a script is silently not running. The examples open in HTMLpad, where you can edit them and see the result straight away.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My page</title>
  <script src="script.js" defer></script>
</head>
<body>
  <button id="hello">Say hello</button>
</body>
</html>

Two rules for the <script> tag:

The path in src works like any other path in HTML: it is read from the folder of the page, and a leading slash means the root of the site. The table in how to link CSS to HTML applies here unchanged.

Write the script in the page

For a single page, the code can go straight into the HTML:

<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Inline script</title></head>
<body>
  <button id="hello">Say hello</button>
  <p id="out"></p>
  <script>
    document.getElementById('hello').addEventListener('click', () => {
      document.getElementById('out').textContent = 'Hello at ' + new Date().toLocaleTimeString();
    });
  </script>
</body>
</html>

Try this example in HTMLpad

This script sits at the end of the body on purpose. A script runs as soon as the browser reaches it, and at that moment only the HTML above it exists. Put the same script in the head without defer and getElementById('hello') finds nothing, because the button has not been read yet.

What defer and async do

Browsers normally stop reading the page when they meet a script, download it, run it, and only then carry on. That is why scripts used to go at the bottom of the body. Two attributes change this for external files:

Attribute Downloads Runs Use it for
none Pauses the page while it downloads Immediately Rarely needed today
defer In the background After the page has been read, in the order written Almost all your own scripts
async In the background As soon as it arrives, in any order Independent scripts such as analytics

With defer, the script can go in the head, start downloading early and still find every element on the page. The MDN reference for the script element covers the details, and the HTML standard's scripting section has a diagram of exactly when each kind runs.

Both attributes only affect scripts with src. On a script written inside the page they do nothing.

Load a library from a CDN

Libraries are often loaded from a public address in the same way:

<script src="https://cdn.jsdelivr.net/npm/some-library@1/dist/library.min.js" defer></script>
<script src="script.js" defer></script>

Order matters: with defer, scripts run in the order they appear, so the library goes first and your code that uses it goes after.

Modules

Modern JavaScript can be split into modules that import each other. Mark the entry file with type="module":

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

Module scripts are deferred automatically. One catch: they do not run when you open the page by double-clicking the file, because browsers block modules on file:// addresses. Open the page through a server, or paste it into an online editor, and they work.

When the script does not run

  1. Open the console. Press F12 and look at the Console tab. A red message usually names the problem and the line.
  2. Check the Network tab. If the script file shows a 404, the path in src is wrong.
  3. Check the timing. "Cannot read properties of null" usually means the script ran before the element existed. Add defer, or move the script to the end of the body.
  4. Check the spelling of ids. getElementById('Hello') and id="hello" do not match; JavaScript is case sensitive.
  5. Check the file type. The file must end in .js and be served as JavaScript. A file saved as script.js.txt will not run.

The quickest way to test a script is to paste the whole page into the HTML sandbox, where errors and changes show up immediately. If the page is part of something bigger, the links guide covers HTML link code, and the guides to editing HTML files and opening HTML files cover the basics.

Frequently asked questions

Where should I put the script tag?

In the <head> with the defer attribute, for external files. Scripts written inside the page usually go at the end of the <body>, so the elements they use already exist.

What is the difference between defer and async?

Both download the script without pausing the page. A deferred script runs after the page has been read, in order. An async script runs as soon as it arrives, in any order, which suits independent scripts such as analytics.

Why does my JavaScript not work when I open the HTML file?

Common causes are a wrong path in src, a script that runs before the elements it needs exist, or module scripts, which browsers block when a page is opened straight from the disk.

Yes. Use a <link rel="stylesheet"> tag for the CSS and a <script> tag for the JavaScript, both in the head. They do not interfere with each other.

More in Open and Edit HTML