ContentsThe library

How a Browser Draws a Page

The Browser Starts Building the Page Before It Has Finished Reading It, and One Tag in the Wrong Place Stops the Whole Production Line

Markup becomes a tree while the bytes are still arriving. This lesson follows that conversion, then shows what happens to it the moment the parser meets a script.

Characters, tokens, nodes

A document arrives as a stream of characters with no structure whatsoever. What

the page needs is a tree, because everything downstream, styling, layout and

painting, is defined in terms of parents and children.

The conversion happens in two stages, and separating them is what makes the

whole thing tractable.

A tokeniser reads characters one at a time and emits tokens. A start tag. An

attribute on that tag. A run of text. An end tag. It is a state machine with

no memory of structure, and it does not know or care whether the document makes

sense.

A tree builder consumes those tokens and maintains a stack of currently open

elements. A start tag pushes a node onto the stack and attaches it as a child

of whatever is beneath. Text attaches to whatever is on top. An end tag pops.

That stack is where nesting actually lives.

FIG 1Half a document and the tree it has already produced
plaintext
bytes received so far

  <html><head><link rel=stylesheet href=/site.css>
  </head><body><h1>Prices</h1><p>From
  ... connection still open, more coming

tree built so far

  html
    head
      link
    body
      h1
        text: Prices
      p
        text: From

open element stack: html, body, p
The paragraph is still open and its text is incomplete, and everything above it is finished and usable. The stack at the bottom is the parser state in full: three entries, which is all that is needed to attach whatever token arrives next.

Building it while it arrives

The important consequence is that none of this waits. A browser receiving the

first kilobyte of a document can tokenise it, build that part of the tree,

resolve styles for it, and in many cases put it on the screen, all while the

rest of the document is still in flight.

FIG 2One document arriving in four pieces
stepbytes innodes in the treewhat the browser did with themon screenwhat happened
114009found the stylesheet and started fetchinnothingThe first packet usually carries the head. Finding the stylesheet reference early is the single most valuable thing in it, because that fetch gates the first paint.
2420031styled the header and worked out its posnothing yet, waiting on the stylesheetThe tree is growing and layout can be computed, but painting waits because applying styles later would mean painting the wrong thing first.
3980074stylesheet arrived, painted the header aheader and textThe first frame. Note that less than half the document has arrived, and the user is already reading.
421000160finished the documentthe whole pageThe last byte changes less than people expect. Most of the perceived load finished several packets earlier.
4 steps
Follow the last column. The page becomes visible at the third row, with under half the bytes received, which is why the order of things in a document matters so much more than its total size.

This is also why a document that streams beats a document that is assembled

first and sent in one piece. The browser can only work on what it has, so

sending the head immediately and the body as it becomes available gives the

browser a head start measured in whole round trips.

Why a script stops everything

Then the parser reaches a script tag, and the production line stops.

FIG 3What the parser does on meeting a script
Three paths from the same tag. The default is the one that stops everything, which is a historical choice rather than a sensible one, and the two attributes exist because almost no modern script actually needs the blocking behaviour.

The cost is easy to underestimate because it is not the running of the script

that hurts. It is the fetch. A script tag in the head of a document, with no

attribute, costs a full round trip during which nothing is parsed, nothing is

styled and nothing can be painted.

FIG 4The three behaviours, and when each is right
blocks parsing while fetfetched alongside parsinruns in document orderruns whenever it arrives
plain script tag1010
marked defer0110
marked async0101
The first marked cell is the problem and the second is the usual answer. Defer keeps the ordering guarantees that most page scripts quietly depend on while removing the blocking fetch entirely, which is why it is the right default and the plain tag almost never is.

Reading ahead for resources

One refinement saves the blocking case from being as bad as it sounds. While

the parser is stopped waiting on a script, a second much simpler scanner runs

ahead through the bytes already received, looking only for things worth

fetching: stylesheets, scripts, images.

It does not build a tree and it does not need to be correct about structure. It

only needs to spot references and start the fetches early, so that when the

parser resumes the resources are already on their way.

FIG 5Where the time goes while the parser is blocked
parser stoppedresources fetching anyway0200400800120120: parser hits the script140140: the scanner has found four more resources460460: script arrives and runs620620: first paintmilliseconds since the document started arriving
The two spans overlap almost completely, and that overlap is the entire value of reading ahead. Without it the second span would begin at 460 and the first paint would land several hundred milliseconds later. The parser is still stopped for a third of a second, which the scanner cannot fix.

Two things follow for anybody writing documents.

References the scanner can see get found early. A stylesheet or image named in

the markup is spotted immediately. One that a script decides to fetch after it

runs cannot be, because the scanner does not execute anything, so that resource

starts a round trip or two later than it needed to.

And the scanner is a mitigation, not a cure. The parser is still stopped, the

tree is still not growing, and nothing can be painted. The correct action is

still to not block the parser in the first place.

So the document has become a tree, incrementally, with one well-understood way

of stalling it. The tree on its own places nothing on the screen, because every

node still needs to know what it looks like. That is the next lesson.

Recap

  • Parsing is incremental: the tree grows from the bytes already received, so the top of a document can be worked on while the bottom is still in flight.
  • A plain script tag stops parsing entirely until the script has been fetched, parsed and run, because the script might change the document being parsed.
  • The two attributes that change this are the single highest-value change available in most documents, and which one to use follows from whether order matters.

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

NextWorking Out What Applies →

The rest of this course

  1. 01The Browser Starts Building the Page Before It Has Finished Reading It, and One Tag in the Wrong Place Stops the Whole Production Lineyou are here
  2. 02Every Element Has a Value for Every Property Whether You Wrote One or Not, and the Browser Will Not Place a Single Box Until It Knows Them Allopening only
  3. 03Four Things Stand Between a Document Arriving and Anything Appearing, and Only One of Them Is Usually the Reason Your Page Is Slowopening only
  4. 04Width Flows Downwards and Height Flows Back Up, Which Is Why the Browser Has to Visit Every Box Twice and Why One Careless Property Makes It Visit Them Foreveropening only
  5. 05The Boxes Are Placed and Nothing Is Visible Yet, Because Drawing Them Is a Separate Job With Its Own Rules and Its Own Surprising Billopening only
  6. 06Everything So Far Described Drawing the Page Once, and the Page Is Drawn Again Sixty Times a Second, Which Changes Which Mistakes Matteropening only
  7. 07You Reached for a Link and the Page Moved and You Pressed Something Else, and the Reason Is That the Browser Was Never Told How Big a Picture Wasopening only
  8. 08Parsing, Styling, Layout, Painting, Your Code and Every Press of a Button Take Turns on a Single Queue, and Whoever Is Holding It Is Holding Everythingopening only

Read alongside