<script>
| Since: | HTML 4(1997) |
|---|
The script element is used to embed JavaScript code inline or to load an external JS file.
Syntax
<!-- Inline JavaScript -->
<script>
console.log("Hello!");
</script>
<!-- Load an external file -->
<script src="app.js"></script>
<!-- Asynchronous loading (defer recommended) -->
<script src="app.js" defer></script>
Common Attributes
| Attribute | Description |
|---|---|
| src | Specifies the URL of an external JavaScript file. If set, any inline code inside the tag is ignored. |
| type | Specifies the MIME type of the script. Defaults to text/javascript if omitted. Use module to load the script as an ES module. |
| defer | Defers script execution until HTML parsing is complete, just before the DOMContentLoaded event. Only applies to external files. |
| async | Downloads the script in parallel with HTML parsing and executes it as soon as the download is complete. Execution order is not guaranteed. |
| module | Setting type="module" treats the script as an ES module. Behaves like defer by default. |
Sample Code
ES modules are a mechanism for splitting and managing JavaScript files. Specifying type="module" enables the use of import/export syntax.
sample_script.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Script Sample</title>
<!-- defer: runs after the DOM is built (recommended in <head>) -->
<script src="main.js" defer></script>
<!-- async: runs immediately after download (use for independent scripts) -->
<script src="analytics.js" async></script>
<!-- ES module -->
<script type="module" src="app.mjs"></script>
</head>
<body>
<p id="message">Loading...</p>
<!-- Inline script (traditionally placed at the end of <body>) -->
<script>
// The DOM exists here, so you can access it directly
document.getElementById("message").textContent = "Loaded!";
</script>
</body>
</html>
Output
When the page loads, the inline script replaces the paragraph text.
Notes
Where you place the script element and how you load it has a significant impact on page load performance. A plain script tag inside <head> blocks HTML parsing, so it is strongly recommended to add the defer attribute when loading external scripts. With defer, the script downloads in the background without blocking parsing and executes after the DOM is fully built.
async is well suited for scripts that operate independently, such as analytics tools or ad tags. Because the execution order of multiple async scripts is not guaranteed, avoid using it for scripts that depend on each other.
Using ES modules (type="module") lets you split your code across files with import/export syntax. Modules behave like defer by default and are only loaded once even if referenced multiple times. On pages where you want to disable script execution entirely, you can use the noscript element to display fallback content.
Comparing Normal, defer, and async Loading
Here is how HTML parsing and script execution timing differ across the three loading modes.
| Mode | Download | HTML parsing | Execution timing | Order guaranteed |
|---|---|---|---|---|
| Normal (no attribute) | Synchronous | Blocked | Immediately after download | Yes |
| defer | Parallel | Not blocked | After DOM is built (before DOMContentLoaded) | Yes |
| async | Parallel | Not blocked | Immediately after download (may run before DOM is ready) | No |
| type="module" | Parallel | Not blocked | Same as defer (default behavior) | Yes |
<!-- Recommended: defer in head (executes when DOM is ready) --> <head> <script src="main.js" defer></script> </head> <!-- Independent analytics or ad scripts: use async --> <script src="https://analytics.example.com/track.js" async></script> <!-- Classic approach: place at end of body (equivalent to defer) --> <body> <!-- content --> <script src="main.js"></script> </body>
Best Practices for Inline Scripts
Patterns for embedding JavaScript directly in HTML without an external file.
<!-- Wait for DOMContentLoaded before touching the DOM (safe even in head) -->
<script>
document.addEventListener("DOMContentLoaded", function() {
var btn = document.getElementById("toggle-btn");
btn.addEventListener("click", function() {
document.getElementById("menu").classList.toggle("open");
});
});
</script>
<!-- Embed JSON data in HTML and read it from JavaScript -->
<script type="application/json" id="app-config">
{"apiUrl": "/api", "version": "1.0", "debug": false}
</script>
<script>
var config = JSON.parse(document.getElementById("app-config").textContent);
console.log(config.apiUrl); // "/api"
</script>
Browser Support
1 and later ○
1 and later ○
3 and later ○
2 and earlier ×
8 ○
7 ○
6 ○
12.1 and later ○
11.1 and earlier ×
2 and later ○
Android Browser
4.4 and later ○
4 and earlier ×* Version data based on MDN.
If you find any errors or copyright issues, please contact us.