Bytes to tokens
A page arrives as bytes: numbers from 0 to 255. The browser decodes them into characters using the page's encoding, usually UTF-8. The first six bytes, 3C 21 44 4F 43 54, become <!DOCT.
The tokenizer reads the characters one at a time and groups them into tokens: a DOCTYPE, start tags like <h1>, end tags like </h1>, and text. Line 3 makes three tokens: <h1>, the text Menu, then </h1>.
Building the tree
Tree building starts with an empty Document node: the root of the tree. The DOCTYPE token becomes its first child, a doctype node. It also tells the browser to use standards mode, not quirks mode.
Line 2 starts with <title>, but there is no html or head element yet. The parser creates both by itself, then puts title inside head. The file may leave these tags out; the tree never does.
The parser keeps a stack of open elements. A start tag pushes its element on top, and every new node goes inside the top one. So Cafe becomes a text node in title, and </title> pops title off.
An h1 cannot go in head. So the parser pops head, creates the body element the file never wrote, and pushes it. Then h1 goes inside body, Menu inside h1, and </h1> pops h1.
<p> is pushed, and Hot becomes text inside p. Then <em> is pushed on top of p, so em becomes a child of p, and mocha goes inside em. </em> pops em; p stays open.
Line 4 never closed its p, which HTML allows. A p cannot hold another p, so when the next <p> arrives, the parser closes the open one first. Then it pushes the new p, and Iced latte goes inside.
The last token is end-of-file. The parser pops every element still open, p, body and html, without a single end tag in the file. The DOM tree is complete.
The living DOM
The DOM is live: a tree of objects in memory that JavaScript can read and change. One line finds the em element and sets its text to cocoa. The page updates at once; the file does not change.
View Page Source shows the file as it was sent. The Elements panel of the developer tools shows the DOM instead: with html, head and body added, both p elements closed, and cocoa in place of mocha.
What happens
;; step 1/11 · bytes
file menu.html (86 bytes)
bytes 3C 21 44 4F 43 54 (hexadecimal)
decode UTF-8: one byte per character for these
0x3C → <
0x21 → !
0x44 → D
0x4F → O
0x43 → C
0x54 → T
The line breaks between tags also become small text nodes that hold only whitespace. The figure leaves them out, as browser developer tools usually do. A real browser parses a page in a few milliseconds and starts building the tree before the last byte arrives. The figure slows it down to one line per step.