JavaScript has to be connected to your HTML before it can do anything. You do that with the <script> element, either by writing code inside it or by pointing it at a separate .js file.
Where you place the script, and a small attribute called defer, make a big difference. A script in the wrong place can slow the page down or fail because the elements it needs haven't loaded yet. This lesson shows the reliable setup used on professional sites, so you can avoid that whole category of bugs.
Writing JavaScript inside a script tag
<script>
console.log("Hi from inside the HTML");
</script>Inline scripts are fine for small experiments and for the examples on this site. On a real website, though, they mix code into the HTML and can't be reused by other pages.
Using an external JavaScript file (recommended)
Put your code in a file such as app.js, then link to it:
<script src="app.js" defer></script>- The
.jsfile contains only JavaScript, with no<script>tags inside it. - One file can be shared by every page, and browsers cache it so later pages load faster.
- A
<script>with asrcmust be empty between its tags. Any code written there is ignored.
The path in src works like a link. app.js looks in the same folder as the HTML file, and js/app.js looks in a folder called js.
defer, async and script placement
The browser reads HTML from top to bottom. A plain <script> in the <head> stops everything until it has downloaded and run, and it can't find elements that haven't been read yet.
| Option | Behaviour |
|---|---|
<script src> at the end of <body> | Runs after the HTML above it. The traditional fix. |
<script src defer> in <head> | Downloads alongside the HTML and runs after the page is read, in order. Recommended. |
<script src async> | Downloads alongside the HTML and runs as soon as it's ready, in any order. Suits independent scripts like analytics. |
<script type="module" src> | Deferred automatically, and lets you use import and export. See Modules. |
A common error: "Cannot read properties of null"
If a script runs before the HTML it needs, document.querySelector() returns null, and the next line fails with an error such as Cannot read properties of null. When you see that message, check two things: that the selector is spelled correctly, and that the script has defer or sits at the end of the body. Most of the time, one of those is the cause.
JavaScript Script Tag Syntax
Script tags
<script> /* inline code */ </script>
<script src="js/app.js" defer></script>
<script type="module" src="js/main.js"></script>JavaScript Script Tag Summary
- JavaScript is added to a page with the
<script>element. - External
.jsfiles are best for real sites because they're reusable and cached. - A script with
srcmust have nothing between its tags. deferruns the script after the HTML is read, in order;asyncruns it as soon as it loads.- A
nullerror often means the script ran before the HTML it needs.
JavaScript Script Tag Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Too early!
The first script runs before the heading exists, so it finds nothing. The second runs after.
<script>
console.log("Before:", document.querySelector("h1"));
</script>
<h1>Welcome</h1>
<script>
console.log("After:", document.querySelector("h1"));
</script>Line-by-line explanation
- Lines 1 to 3When this runs, the browser hasn't read the
<h1>yet, so the result isnull. - Lines 7 to 9By now the heading exists, so we get the element.
A typical page setup
This is what a real page looks like. (The editor runs both files together, just like the browser would.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Weather</title>
<script src="data:text/javascript,console.log('app.js loaded (deferred)')" defer></script>
</head>
<body>
<h1>Today: Sunny ☀️</h1>
<script>
console.log("Inline script at the end of body");
</script>
</body>
</html>Line-by-line explanation
- Line 6In a real project this would be
src="app.js".defermeans "download now, run when the HTML is ready". - Lines 10 to 12An inline script at the end of the body runs first, because deferred scripts wait until parsing is finished.
Common JavaScript Script Tag Mistakes
Code inside a script with src
<script src="app.js">
console.log("This never runs");
</script><script src="app.js"></script>
<script>
console.log("Separate script");
</script>When src is set, the content between the tags is ignored.
Script tags inside a .js file
<script>
console.log("Hello");
</script>console.log("Hello");A .js file is pure JavaScript. HTML tags in it cause a syntax error on the first line.
JavaScript Script Tag in Real Projects
How real projects load JS
Most modern sites load one or a few bundled files with defer or type="module", created by tools like Vite or webpack. Third-party scripts like analytics and chat widgets usually use async so they never slow the page down.
JavaScript Script Tag Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Load app.js so it runs after the HTML is parsed.
<script ="app.js" ></script>
src points at the file; defer delays running until the HTML is ready.What will a plain script in the <head> get from document.querySelector("button") when the button is in the body?
Which attribute makes scripts run in order after parsing?
defer keeps order and waits for the HTML.The script runs before the paragraph exists, so it fails. Move the script so it can find the paragraph (don't change the JavaScript itself).
<script>
document.querySelector("#greet").textContent = "Hello from JS!";
</script>
<p id="greet">Waiting…</p>- The paragraph was updated
- No errors
JavaScript Script Tag Quiz
3 questions to check what you learned.
What file extension do JavaScript files use?
- .js
- .java
- .jsx only
- .script
What's the difference between
asyncanddefer?- There is none
deferruns in order after parsing;asyncruns as soon as it downloads, in any orderasyncis faster in all casesdeferonly works on inline scripts
Can a
<script src="x.js">also contain code between its tags?- No, the inline code is ignored
- Yes, both run
- Only comments
- Only if type="module"
JavaScript Script Tag Challenge
Count the paragraphs
Write a script after the HTML that logs exactly Paragraphs: 3 by counting the <p> elements with document.querySelectorAll("p").length.
<p>One</p>
<p>Two</p>
<p>Three</p>// your code here
- Logs "Paragraphs: 3"
console.log("Paragraphs:", …) prints a label and a value separated by a space.