Skip to Content
Course content

4: Linking and Loading Scripts on a Page

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've been playing around with JavaScript in isolated environments or small snippets. But when you actually start building a project, you have to figure out where that code lives in your HTML. It sounds trivial—just drop a script tag in, right? That's exactly where most people hit their first "why is this not working" wall.

The "Head" Trap and the Null Reference

The most intuitive place to put a script is in the <head>. It feels organized; you're declaring your dependencies up top before the content starts. But here is the problem: browsers parse HTML from top to bottom. When the browser hits a <script> tag, it stops everything. It pauses the construction of the page, fetches the script, and executes it immediately.

Imagine you're building a simple "Dark Mode" toggle. You have a button in your HTML with the class .theme-toggle, and your JavaScript looks like this:

const btn = document.querySelector('.theme-toggle');
btn.addEventListener('click', () => {
    document.body.classList.toggle('dark-theme');
});

If this script is in the <head>, it runs before the browser has even reached the <body>. At the moment the code executes, .theme-toggle doesn't exist yet. The browser returns null, and your console screams with a TypeError: cannot read property 'addEventListener' of null. I've spent more hours than I'd like to admit debugging this exact issue early in my career because I assumed the browser "just knew" to wait.

Moving to the Bottom of the Body

The traditional "fix" for this was to move the script tag to the very end of the <body>, just before the closing </body> tag. By the time the browser reaches the script, it has already parsed the entire DOM. Your button is there, your images are loaded, and querySelector actually finds what it's looking for.

This works, and you'll see it in plenty of older tutorials. However, it's not ideal. If your JavaScript file is huge, the user is staring at a fully rendered page that looks finished but is completely non-interactive because the browser is still downloading that giant script at the bottom. It creates a weird "dead zone" where the UI is visible but nothing happens when you click.

Modern Loading with Defer and Async

Nowadays, we have better tools: the defer and async attributes. These allow us to put our scripts back in the <head> (where they are easy to find) without blocking the page from rendering.

  • defer: This is usually what you want. It tells the browser: "Start downloading this script in the background, but don't execute it until the HTML is fully parsed." It's the gold standard for your main application logic because it ensures the DOM is ready and it maintains the order of your scripts.
  • async: This is different. It downloads the script in the background, but the second it finishes downloading, it pauses the HTML parser to execute the script. I only use this for third-party scripts that don't care about your DOM or other scripts—think Google Analytics or an ad pixel.

If you're choosing between them, stick with defer. It gives you the organization of the head with the safety of the bottom-of-the-body approach, and it's much more performant for the end user.




📋 Practical Task

Fixing the Broken Theme Switcher

You've been handed a project where the "Dark Mode" toggle is completely broken. The JavaScript file theme-engine.js is correctly written to find a button with the ID #mode-switch and toggle a class on the body, but the console is throwing a TypeError because the script is running before the button exists.

Your Goal: Modify the HTML structure provided below. Move or modify the script loading mechanism so that the JavaScript executes only after the DOM has been fully parsed, ensuring the button is available when the script runs, while keeping the script reference in the <head> section for organization.

<!DOCTYPE html>
<html>
<head>
    <title>Theme Page</title>
    <script src="theme-engine.js"></script>
</head>
<body>
    <button id="mode-switch">Switch Theme</button>
</body>
</html>
Rating
0 0

There are no comments for now.

to be the first to leave a comment.