Browser Internals: What Happens Before Your JavaScript Runs
Introduction:
Most developers think about JavaScript execution as the starting point of a web page. But by the time your first line of JavaScript runs, the browser has already done significant work.
Understanding what happens before execution helps you write faster, more predictable code and debug problems that appear before your application even starts.
The Browser Receives Raw Bytes First:
When a user navigates to a URL, the browser receives raw bytes over the network. Those bytes are decoded into characters based on the encoding specified in the response headers.
This step seems trivial but matters — incorrect encoding declarations cause rendering issues that have nothing to do with your JavaScript or CSS.
HTML Is Parsed Into a DOM:
The browser parses the decoded HTML and constructs the Document Object Model. This is a tree representation of every element on the page.
Parsing is not instant. The browser reads HTML top to bottom, and anything that interrupts that process delays when your JavaScript becomes available.
JavaScript Blocks HTML Parsing by Default:
When the parser encounters a <script> tag without async or defer, it stops parsing HTML entirely. It fetches the script, executes it, and only then continues building the DOM.
This is why scripts placed in the <head> without defer can significantly delay page rendering. The browser is not being slow — it is following a deliberate parsing model.
CSS Has to Be Resolved Before Scripts Execute:
Before executing JavaScript, the browser also needs to build the CSSOM — the CSS equivalent of the DOM. JavaScript can query and modify styles, so the browser waits for CSS to be fully parsed first.
A slow stylesheet blocks script execution even if your JavaScript has nothing to do with styles. CSS and JavaScript are more tightly coupled at load time than most developers realise.
The Render Tree Combines DOM and CSSOM:
Once both the DOM and CSSOM are ready, the browser combines them into a render tree. This tree contains only the visible elements and their computed styles.
Layout and painting happen after this — the browser calculates where each element sits on the page and then draws pixels to the screen. JavaScript execution fits into this sequence, not before it.
async and defer Change the Sequence:
The async attribute tells the browser to fetch the script in parallel but execute it as soon as it arrives, potentially interrupting parsing. The defer attribute fetches in parallel but waits until parsing is complete before executing.
Choosing between them incorrectly causes subtle ordering bugs that are difficult to trace because they depend on network timing, not just code logic.
Conclusion:
JavaScript does not run in isolation. It runs at a specific point in a sequence that includes network requests, byte decoding, HTML parsing, CSS resolution, and render tree construction.
Understanding this sequence is not an academic exercise. It directly influences how you structure scripts, load stylesheets, and diagnose performance problems that appear before your application logic ever runs.
If this article helped you, you can support my work on AW Dev Rethought. Buy me a coffee
Enjoyed this post?
Stay in the loop
New posts + weekly digest, straight to your inbox.
Create a free account
- Save posts to your vault
- Like posts & build history
- New-post alerts
No comments yet. Be the first to comment!