EN/CH Mode
BRUCE_FE Interview Notes - Page Loading - Script Placement & defer/async Attributes
In-depth analysis of optimal JavaScript script tag placement and the timing and effects of defer and async attributes to help you enhance web performance and user experience.
EN/CH Mode

Lazy to read articles? Then watch videos!
Script Tag Placement
The loading and execution timing of JavaScript in a webpage directly affects user experience. The behavior of script tags varies depending on their placement:
- 1.Placed in
<head>: The browser will download and execute JavaScript before parsing HTML, which blocks page rendering and slows down page loading. - 2.Placed before
</body>: The browser will render the HTML content first, then download and execute JavaScript. Users can see the page content faster, but some functionality may not be available before JavaScript loads.
Purpose of the defer attribute
The defer attribute allows the script to download alongside HTML parsing, but delays execution until HTML parsing is complete:
<script defer src="script.js"></script>defer characteristics:
- •Does not block HTML parsing and rendering
- •Guarantees execution in the order specified in HTML
- •Executes before the DOMContentLoaded event
- •Only effective for external scripts (with src attribute)
Suitable scenarios:
- •Scripts that need to execute after the DOM is fully loaded
- •Scripts dependent on execution order
- •Scripts that don't need immediate execution
Purpose of module scripts
The type="module" attribute tells the browser to treat the script as a JavaScript module, supporting import/export syntax:
<script type="module" src="app.js"></script>Differences between module and defer:
- •Module scripts have defer behavior by default, no need to add the defer attribute
- •Modules support import/export syntax, while defer scripts don't
- •Module scripts automatically use strict mode
Scenarios where module must be used instead of defer:
- •When import/export syntax is needed
- •When code needs to be modularized for better maintainability
// math.js (模組)
export function add(a, b) {
return a + b;
}
// app.js (使用模組)
import { add } from './math.js';
document.addEventListener('DOMContentLoaded', () => {
console.log(add(2, 3)); // 輸出: 5
});Purpose of the async attribute
The async attribute allows the script to download alongside HTML parsing, and executes immediately after download completes:
<script async src="script.js"></script>async characteristics:
- •Does not block HTML parsing and rendering
- •Does not guarantee execution order; executes in download completion order
- •Pauses HTML parsing during execution
- •Only effective for external scripts
- •May not execute before the DOMContentLoaded event
Suitable scenarios:
- •Independent functionality that doesn't rely on other scripts
- •Third-party scripts like analytics and advertisements
- •Scripts that don't need to manipulate the DOM
Visual Comparison
The diagram below illustrates the behavioral differences between script loading methods:
──────────────────────────── Timeline ────────────────────────────>
【No Attribute - Before </body>】
HTML Parsing: ■■■■■■■■■■■■■■■■■■■■■■■■■■HTML Parsing■■■■■■■■■■■■■■■■■■
↑ ↑
Start Complete
Script: ■■■Download+Execute■■■
↑ ↑
Start Complete
<body>
<!-- Content -->
<script src="script.js"></script> <!-- Traditional approach -->
</body>
--------------------------------
【defer】
HTML Parsing: ■■■■■■■■■■■■■■■■■■■■■■■■■■HTML Parsing■■■■■■■■■■■■■■■■■■→DOM Ready
↑ ↑ ↑
Start Complete DOMContentLoaded
Script: ■■■■■■■■■■■■■Download■■■■■■■■■■■■■ ■■■Execute■■■
↑ ↑ ↑ ↑
Start Complete Start Complete
<head>
<script defer src="script.js"></script> <!-- Modern recommended approach -->
</head>
--------------------------------
【async】
HTML Parsing: ■■■■■■■■■■■■■■■■■Pause■■■■■■■■■Remaining Parse■■■■■■■■■■■
↑ ↑ ↑ ↑
Start Pause Resume Complete
Script: ■■■■■■■Download■■■■■■■■■Execute■■■
↑ ↑ ↑
Start Complete Execute Complete
<head>
<script async src="script.js"></script> <!-- Suitable for independent scripts -->
</head>Best Practices
- 1.Modern recommended approach: Place scripts with the
deferattribute in the<head><head> <script defer src="main.js"></script> </head>This allows scripts to start downloading early while not blocking HTML parsing, improving the page loading experience.
- 2.Independent scripts: Use the
asyncattribute<head> <script async src="analytics.js"></script> </head> - 3.Traditional approach: Place scripts before
</body><body> <!-- 頁面內容 --> <script src="main.js"></script> </body> - 4.Core functionality and time-sensitive: No attributes, directly in
<head><head> <script src="critical.js"></script> </head>
🔥 Common Interview Questions
(1) What's the difference between defer and async?
Answer: The main differences are in execution timing and order:
- •
defer: Executes after HTML parsing is complete, in the order specified in the HTML - •
async: Executes immediately after download completion, doesn't guarantee order, interrupts HTML parsing
<!-- defer example: executed in order -->
<head>
<script defer src="first.js"></script>
<script defer src="second.js"></script>
<!-- first.js will always execute before second.js -->
</head>
<!-- async example: order not guaranteed -->
<head>
<script async src="analytics.js"></script>
<script async src="ads.js"></script>
<!-- whichever downloads first executes first -->
</head>(2) Why is it not recommended to place all scripts in <head> without using defer or async?
Answer: This forces the browser to download and execute all JavaScript before rendering the page, resulting in extended white screen time, severely impacting First Contentful Paint (FCP) and user experience.
<!-- Not recommended: blocks rendering -->
<head>
<script src="large-library.js"></script>
<script src="app.js"></script>
<script src="components.js"></script>
<!-- Page waits for all scripts to download and execute before rendering -->
</head>
<!-- Recommended: use defer -->
<head>
<script defer src="large-library.js"></script>
<script defer src="app.js"></script>
<script defer src="components.js"></script>
<!-- HTML parsing isn't blocked, scripts execute before DOMContentLoaded -->
</head>(3) When to choose defer, and when to choose async?
Answer:
- •Choose
defer: When scripts need to manipulate the DOM or depend on execution order - •Choose
async: When scripts are completely independent and order doesn't matter, like analytics tools or ads
<!-- Use defer when: DOM and execution order are needed -->
<head>
<script defer src="jquery.js"></script>
<script defer src="jquery-plugin.js"></script>
<script defer src="app.js">
// This script depends on jquery and jquery-plugin
// and may need to manipulate DOM elements
</script>
</head>
<!-- Use async when: functionality is independent -->
<head>
<script async src="google-analytics.js"></script>
<script async src="facebook-pixel.js"></script>
<!-- These scripts are independent, don't need specific order, and don't rely on DOM -->
</head>(4) How does module script (type="module") loading behavior work?
Answer: Module scripts have defer behavior by default, even without explicitly setting the defer attribute. If async behavior is desired, the async attribute must be explicitly added.
<!-- Has defer behavior by default -->
<head>
<script type="module" src="app.js"></script>
<!-- Equivalent to <script type="module" defer src="app.js"></script> -->
<!-- If async behavior is needed, must be explicitly specified -->
<script type="module" async src="independent-module.js"></script>
</head>(5) Can inline scripts use defer or async attributes?
Answer: No. The defer and async attributes are only effective for external scripts (script tags with a src attribute) and have no effect on inline scripts.
<!-- Invalid: defer and async don't work on inline scripts -->
<head>
<script defer>
console.log("This defer attribute has no effect");
// This script executes immediately, blocking HTML parsing
</script>
<script async>
console.log("This async attribute has no effect");
// This script also executes immediately
</script>
<!-- Valid: external scripts can use defer or async -->
<script defer src="external.js"></script>
</head>