Question
When adding JavaScript to an HTML document, where should the <script> tags and JavaScript code be placed?
I understand that placing a normal script in the <head> can delay page parsing, while placing it at the start of the <body> can also delay the visible page content. Does that make the end of the <body> the correct location for scripts?
I am using jQuery, but I would also like to understand the general HTML and JavaScript guidance, including where event-handling code should go instead of inline calls in <a> elements.
Short Answer
You will learn how ordinary, defer, async, and module scripts affect HTML parsing; when to put scripts in the document head or at the end of the body; and how to safely run code that needs page elements or jQuery.
Concept
A browser generally processes an HTML document from top to bottom. When it encounters a classic external script such as:
<script src="app.js"></script>
it normally pauses HTML parsing, downloads the file if necessary, and executes it before continuing. This behavior matters because it can delay the browser from reaching and displaying the rest of the page.
The best placement depends on the script's loading behavior and dependencies:
- Modern default: place external scripts in
<head>withdefer. The browser can download the script while it continues parsing HTML. The script runs after parsing is complete, in document order. - Alternative: put classic scripts just before
</body>. By that point, most page elements have already been parsed, so the script can usually access them. This is still valid and is common in older codebases. - Use
asynconly for independent scripts. An async script runs as soon as it finishes downloading, so its execution order is not predictable. - Use
type="module"for JavaScript modules. Module scripts are deferred by default, making head placement appropriate in most cases.
Script placement is not only about rendering speed. It also determines whether the elements your code needs already exist when the code runs. If code queries #save-button before the browser has parsed that element, the query may return .
Mental Model
Think of HTML parsing as a reader moving through a recipe from top to bottom.
A classic <script> is like an instruction that says: stop reading, fetch this extra page if needed, perform every instruction on it, then continue. If that instruction appears near the start, the reader cannot reach the rest of the recipe yet.
A deferred script is like a note that says: fetch this page in the background, but read and perform it after you finish the main recipe. The reader keeps moving through the HTML, and the script runs when the document is ready.
An async script is like a delivery that must be handled immediately whenever it arrives. It is useful for independent tasks, but it is not dependable when one script must run before another.
Syntax and Examples
A modern, reliable pattern is to put external JavaScript in the document <head> and use defer:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Profile page</title>
<script src="app.js" defer></script>
</head>
<body>
<h1>Profile</h1>
<button id="save-button">Save</button>
</body>
</html>
// app.js
const saveButton = .();
saveButton.(, {
.();
});
Step by Step Execution
Consider this document:
<head>
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
</head>
<body>
<a id="help-link" href="/help">Help</a>
</body>
// app.js
$("#help-link").on("click", (event) => {
event.preventDefault();
console.log("Opening help");
});
Execution proceeds as follows:
- The browser starts parsing the
<head>. - It sees
jquery.jswith and begins downloading it. It does stop parsing the HTML.
Real World Use Cases
- Application UI code: Load a page's JavaScript with
deferso buttons, forms, and navigation elements can be selected after parsing. - Libraries and dependent code: Load a library first and an application file second using two deferred scripts, preserving dependency order.
- Analytics or advertising: Load an independent third-party tracking script with
asyncwhen its execution order does not affect core functionality. - JavaScript applications: Use
<script type="module" src="main.js"></script>to load an application's entry module from the head. - Legacy server-rendered pages: Place classic scripts before
</body>when changing the head markup or build setup is difficult. - Inline page data: Put small JSON configuration data in a non-executing script element, then read it from application code when needed.
<script id="page-data" type="application/json">
{"userId": 42, "theme": "dark"}
</script>
Real Codebase Usage
In production projects, developers usually separate JavaScript into external files and use a consistent loading strategy.
Preferred baseline
A common HTML entry point is:
<head>
<script src="/assets/app.js" defer></script>
</head>
This avoids blocking HTML parsing while keeping script declarations in one predictable location.
Event listeners instead of inline attributes
Avoid mixing behavior into markup:
<!-- Avoid -->
<a href="/help" onclick="openHelp(); return false;">Help</a>
Use semantic HTML plus JavaScript:
<a id="help-link" href="/help">Help</a>
helpLink = .();
helpLink.(, {
event.();
();
});
Common Mistakes
Using a blocking script at the top of the page
<head>
<script src="app.js"></script>
</head>
A classic script without defer or async can pause parsing while it downloads and runs. Prefer defer for page code:
<script src="app.js" defer></script>
Adding async to scripts with dependencies
<script src="jquery.js" async></script>
<script src="app.js" async></script>
This is broken when uses jQuery. Either file may execute first. Use for both, in dependency order.
Comparisons
| Approach | HTML parsing | Execution order | Best use |
|---|---|---|---|
Classic <script src="..."> in <head> | Blocks parsing while loading/executing | Document order | Rarely ideal for page scripts |
Classic script before </body> | Parsing has reached page content first; script still blocks when encountered | Document order | Simple legacy pattern |
<script defer src="..."> in <head> | Does not block parsing | Preserved among deferred scripts | Best general choice for dependent page scripts |
<script async src="..."> | Does not block parsing | Not guaranteed |
Cheat Sheet
<!-- Recommended for ordinary page JavaScript -->
<head>
<script src="app.js" defer></script>
</head>
<!-- Dependencies: preserve order with defer -->
<head>
<script src="library.js" defer></script>
<script src="app.js" defer></script>
</head>
<!-- Modern module entry point; deferred by default -->
<head>
<script type="module" src="main.js"></script>
</head>
FAQ
Should script tags go in the head or body?
For most external application scripts, place them in the <head> with defer. Placing a classic script just before </body> is also valid, especially in older pages.
Is it bad to put a script in the head?
A normal classic script in the head can block HTML parsing. A deferred external script in the head is usually a good choice because it downloads without blocking parsing and runs after parsing.
Should I use defer or async?
Use defer for page code, ordered libraries, and scripts that need the DOM. Use async only for independent code whose execution order does not matter.
Does defer guarantee that the DOM exists?
For a normal document, deferred scripts run after HTML parsing is complete, so elements in that parsed document are available. Dynamically added elements may still require delegation or later initialization.
Do module scripts need the defer attribute?
Usually no. <script type="module" src="main.js"> is deferred by default. Adding defer is unnecessary.
How do I load jQuery and my jQuery code safely?
Load jQuery first, then your application script. Add to both scripts so the DOM is parsed first and their order is preserved.
Mini Project
Description
Build a small page that displays a task list and lets a user mark tasks as complete. The project demonstrates a deferred script in the head, DOM selection after parsing, and event listeners instead of inline JavaScript attributes.
Goal
Create a task list where clicking a task toggles its completed appearance and updates a status message.
Requirements
Include an external JavaScript file loaded from the document head with defer.
Create at least three task buttons in the HTML.
Toggle a done CSS class when a task is clicked.
Show the number of completed tasks in a status message.
Do not use inline onclick attributes.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.