Linking JavaScript to HTML
A .js file sitting in your project folder does nothing until it is linked to an HTML page. The browser needs to know the file exists, where to find it, and when to run it. That is the job of the <script> tag.
The script tag
Section titled “The script tag”The <script> tag connects a JavaScript file to an HTML page:
<script src="/scripts/main.js"></script>The src attribute points to the JavaScript file. The path can be relative (./scripts/main.js) or absolute (/scripts/main.js). Unlike CSS <link> tags, <script> uses an opening and closing tag — do not self-close it.
You can also write JavaScript directly inside a <script> tag without a src attribute:
<script> console.log('Inline JavaScript');</script>This is called inline JavaScript. It works, but external files are almost always better — they are cached by the browser, keep HTML clean, and can be shared across multiple pages.
Where to place the script tag: the problem
Section titled “Where to place the script tag: the problem”The browser reads HTML from top to bottom. When it hits a <script> tag, it stops parsing the HTML, fetches the file, executes it, and then resumes.
This creates a problem. If you put your <script> in the <head> without any special attributes, your JavaScript runs before the rest of the HTML has been parsed. Any code that tries to find a button, a nav, or a form will find nothing — the elements do not exist yet.
<head> <script src="/scripts/main.js"></script> <!-- Runs before body exists --></head><body> <nav>...</nav> <!-- Not accessible when the script runs above --></body>This causes the classic beginner error: Cannot read properties of null.
The defer attribute
Section titled “The defer attribute”defer tells the browser: download the script in the background while HTML is still being parsed, then execute it after all the HTML is ready.
<head> <script src="/scripts/main.js" defer></script></head>With defer:
- The file downloads in parallel — no render blocking
- The script runs after the entire HTML document has been parsed
- Multiple deferred scripts run in order
Use defer for virtually all external scripts. It is the modern, correct default.
The async attribute
Section titled “The async attribute”async also downloads the script in the background, but executes it the moment it finishes downloading — regardless of whether the HTML is fully parsed yet.
<script src="/scripts/analytics.js" async></script>The key difference: defer respects order and waits for HTML; async does not. Use async only for scripts that do not interact with the DOM and do not depend on other scripts — analytics trackers and ad scripts are typical examples.
For main.js and any script that touches the page, always use defer.
Summary: placement and attribute rules
Section titled “Summary: placement and attribute rules”| Approach | When to use |
|---|---|
<head> with defer | Default for all external scripts |
<head> with async | Analytics and scripts with no DOM dependency |
Bottom of <body> | Legacy approach; defer is preferred |
Inline <script> | Small, page-specific snippets only |
Linking main.js to the STO site
Section titled “Linking main.js to the STO site”In lesson 03 you added the <script> tag to index.html. The STO site is a plain static HTML site — there is no shared layout file that automatically applies to every page. To load main.js on every page, the tag must be added to each HTML file individually.
Add the following inside the <head> of each STO page, just before </head>:
<script src="scripts/main.js" defer></script>The STO site has six pages — open each one and add the tag:
index.html(already done in lesson 03)about.htmltours.htmlblog.htmlblog-article.htmlcontact.html
This repetition is a real limitation of static sites. Frameworks like Astro, React, and others solve it with shared layout components — but understanding the manual approach first makes the framework solution much easier to appreciate.
Exercise
Section titled “Exercise”- Add
<script src="scripts/main.js" defer></script>to the<head>of every STO HTML file that does not already have it. - Open
index.htmlin Chrome. - Open DevTools → Network tab.
- Reload the page. Find
main.jsin the list of requests and confirm it loaded. - Switch to the Console tab. Confirm the log message from
main.jsappears. - Open
about.htmldirectly in Chrome. Confirm the same log message appears in the Console — this verifies the script is linked on that page too.
- The
<script src="..." defer></script>tag in the<head>is the correct, modern way to load an external script. - Without
defer, a script in the<head>blocks HTML parsing and runs before the page is ready. deferdownloads the script in parallel and executes it after the HTML is fully parsed — use it for virtually all scripts.asyncexecutes immediately when downloaded, without waiting for HTML — use it only for analytics and non-DOM scripts.- In the STO static site, add
<script src="scripts/main.js" defer></script>to the<head>of each HTML file individually — static sites have no shared layout, so there is no single place to add it once.