Suggest an editImprove this articleRefine the answer for “Scripts in head”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)A script with no attributes in `<head>` **stops HTML parsing** until it fully loads and runs, because the browser cannot know whether it will change the DOM (`document.write`, `innerHTML`) and has to wait for it to finish. **Key point:** to avoid the block, use `<script defer>` (loads in parallel, runs after the DOM is built), `<script async>` for independent scripts, or move `<script>` to the end of `<body>`.Shown above the full answer for quick recall.Answer (EN)Image## 1. How the browser loads a page When the browser receives an HTML document, it performs these steps: 1. **Parses the HTML line by line**, top to bottom. 2. When it hits a `<link>` tag (CSS), it stops and loads the stylesheet, because styles affect rendering. 3. When it hits a `<script>` tag, it **stops parsing** in order to **run the JS**. > Why? Because a script can change the DOM itself, or even remove elements that come later in the HTML (`document.write`, `innerHTML`, and so on). So the browser has to **wait for the JS to finish running** before continuing to parse. --- ## 2. What happens if script is in head ```javascript <head> <script src="main.js"></script> </head> <body> <h1>Hello!</h1> </body> ``` Step by step: 1. The browser starts reading the HTML. 2. It reaches `<script src="main.js">`. 3. It **stops parsing** the HTML. 4. It waits for `main.js` to load over the network (can take hundreds of milliseconds). 5. It runs the script. 6. Only then does it continue parsing the rest of the page. During all that time: - the DOM has not been built yet, - the user sees no content, - the FCP / LCP metrics suffer. --- ## 3. Problems this causes | Problem | What happens | |---|---| | The HTML parser is blocked | The page does not build until the JS loads | | A long "white page" | The user sees emptiness | | Slow networks -> worse UX | Especially on 3G/4G | | Worse Core Web Vitals | LCP, FCP, TTI, TBT all grow | | Scripts get in the way of critical CSS | Critical styles do not apply in time | --- ## 4. How to avoid blocking ### Way 1. defer ```javascript <script src="main.js" defer></script> ``` **What it does:** - The script **loads in parallel** with the HTML, - but only runs **after the DOM is fully parsed** (before `DOMContentLoaded`). No blocking. Guaranteed execution order (in sequence). Ideal for most cases. --- ### Way 2. async ```javascript <script src="analytics.js" async></script> ``` **What it does:** - The script **loads asynchronously** (in the background), - and **runs as soon as it loads**, without waiting for others. Used for *independent scripts* (analytics, ads, pixels). Execution order is not guaranteed. --- ### Way 3. Moving scripts to the end of body ```javascript <body> ... <script src="main.js"></script> </body> ``` By the time the JS loads: - the HTML is fully parsed, - the user already sees content, - the JS runs as the last step. This is the "old" but working way. In modern HTML, `<script defer>` has replaced it. --- ## 5. How to handle it in production | Script type | Where and how to include it | |---|---| | Main logic (UI, SPA) | `<script src="main.js" defer>` | | Analytics, ads | `<script src="ga.js" async>` | | Inline initialization | `<script>` inside `<head>` (small initialization) | | Legacy libraries | Better to move to the end of `<body>` | --- ## 6. Impact on metrics | Metric | What gets worse with scripts in head | |---|---| | **FCP (First Contentful Paint)** | The first content shows up later | | **LCP (Largest Contentful Paint)** | The main element renders later | | **TTI (Time to Interactive)** | JS blocks the main thread | | **TBT (Total Blocking Time)** | Grows because of heavy synchronous scripts | --- ## 7. Illustration (in words) ```javascript HTML: <head> -> <script> -> <body> ↓ loading [======= JS is loading =======] ↓ execution [======= JS is running =======] ↓ only then is the DOM built ``` With `defer`, loading happens **in parallel**, and execution happens **at the end**: ```javascript HTML and JS load together -> DOM built -> JS runs ``` --- ## Short summary | Scenario | What it does | Consequence | |---|---|---| | `<script>` in head with no attributes | Blocks parsing | Slow | | `<script defer>` | Loads in parallel, runs after the DOM | Optimal | | `<script async>` | Loads and runs independently | Only for independent scripts | | `<script>` at the bottom of body | Waits until the end of the HTML | No blocking |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.