How to Link JavaScript to HTML
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.
Link an external script file
<!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:
- Always write the closing tag.
<script src="script.js"></script>is correct; a self-closing<script src="script.js" />is not, and the browser will treat everything after it as part of the script. - Put nothing between the tags when you use
src. Code inside a script that also hassrcis ignored.
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>
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
- Open the console. Press F12 and look at the Console tab. A red message usually names the problem and the line.
- Check the Network tab. If the script file shows a 404, the path in
srcis wrong. - 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. - Check the spelling of ids.
getElementById('Hello')andid="hello"do not match; JavaScript is case sensitive. - Check the file type. The file must end in
.jsand be served as JavaScript. A file saved asscript.js.txtwill 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.
Can I link JavaScript and CSS in the same page?
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.
