Lesson 05 / 25
Document Type and Root Structure
The doctype declaration's function, the root element's language declaration, the head/body split, and unwritten elements being built by the parser.
Contents
The previous topic established how the document is processed. This topic establishes how the document is written, and starts with the outermost structure: the declarations that sit in every HTML document’s first lines and are, most of the time, copied without a second thought.
Each of these declarations answers a specific problem. Knowing which problem each one answers also decides which are mandatory and which are conditional.
The Doctype Declaration
A document’s first line is the form <!doctype html>. This is not an element; it is a
declaration given to the parser, and it has a single job: choosing the parsing mode.
The reason this mode exists is backward compatibility. Some of the web’s early documents were written against rules that only became standard later; processing those documents with the standard rules would break their appearance. The solution was to define two modes: standards mode for documents that follow the standard rules, quirks mode for older documents. The doctype declaration is the switch that chooses which mode a document is processed in.
The declaration’s absence is not a flaw, it is a different request: the parser processes the document in quirks mode, and that mode’s rules are not the rules expected today. The differences sit mostly in the presentation layer — how box width is computed, for example — and are not covered in this course. The rule is clear: the declaration is written.
The declaration itself carries no other information. It has no version number, no schema address, no language identity; it only chooses the mode.
The Root Element and Language Declaration
A single root element comes after the doctype declaration: html. It is the only child after
the document node in the tree, and the whole document sits inside it.
There is one attribute that must be written on the root element: lang. It declares the
document’s natural language, and its effect is not visual.
<!doctype html> <html lang="en">
The language declaration affects at least four decisions. Screen readers learn from it which language’s pronunciation rules to read the text with; a wrongly declared language makes text unreadable. Spell checking and hyphenation-based line-breaking rules depend on the language. Search indexers classify the document by language. Some typographic conventions — the form quotation marks take, for example — vary by language.
If part of the document is in a different language, that part is redeclared on the element carrying it; the attribute is inherited and the nearest declaration applies.
<p>The station's Spanish name is <span lang="es">Estación Ladera Norte</span>.</p>
Head and Body
The root element has two children, and their order never changes: head, then body.
The head section (head) carries information about the document: its title, character
encoding declaration, links to external resources, metadata. This content is not painted. Its
only visible effect is the title element being used in the browser tab and in bookmarks.
The body section (body) carries the document’s readable content. Everything that gets
painted is here.
Even though the split looks sharp, there is one element that sits on the boundary: script.
A script element can be found in either section, and where it is found decides the loading
behavior seen in the previous lesson.
Unwritten Elements Are Built
Even when the html, head, and body elements’ tags are not written, the parser builds
them. Which element goes into the head section and which goes into the body is decided by a
defined rule: the moment an element that does not belong to the head section is seen, the head
section closes and the body begins.
// root.mjs — placement of unwritten html/head/body elements const HEAD_CONTENT = new Set(["title", "base", "link", "meta", "style", "script", "noscript"]); function place(text) { const tokens = [...text.matchAll(/<\/?([a-z0-9!]+)[^>]*>/g)] .map((e) => ({ name: e[1], closing: e[0][1] === "/" })); const head = []; const body = []; let doctype = null; let mode = "head"; for (const token of tokens) { if (token.name === "!doctype") { doctype = "html"; continue; } if (token.closing) continue; if (["html", "head", "body"].includes(token.name)) continue; if (mode === "head" && HEAD_CONTENT.has(token.name)) head.push(token.name); else { mode = "body"; body.push(token.name); } } return { doctype, head, body }; } const documents = { "fully written": `<!doctype html><html lang="en"><head><meta charset="utf-8"> <title>North Slope</title></head><body><h1>North Slope</h1><p>1840 m</p></body></html>`, "root elements not written": `<!doctype html><meta charset="utf-8"> <title>North Slope</title><h1>North Slope</h1><p>1840 m</p>`, "no doctype": `<title>North Slope</title><h1>North Slope</h1>`, }; for (const [name, doc] of Object.entries(documents)) { const { doctype, head, body } = place(doc); console.log("[" + name + "]"); console.log(" doctype :", doctype ?? "none -> quirks mode"); console.log(" head :", head.join(", ")); console.log(" body :", body.join(", ")); }
[fully written] doctype : html head : meta, title body : h1, p [root elements not written] doctype : html head : meta, title body : h1, p [no doctype] doctype : none -> quirks mode head : title body : h1
The first two documents were written differently and produced the same structure. The third is split by the same rule but will be processed in a different mode.
One consequence of this rule is that an element that does not belong to the head section
cannot accidentally end up written into it: even if written there, it is carried over to the
body. The reverse holds too — a title element written in the middle of the body does not
travel back to the head section, it stays in the body and does not do the job expected of it.
The parser’s tolerance is not about silently fixing writing mistakes, it is about producing a
defined result.
Writing the tags anyway still has a justification: readability and where attributes go. The
lang attribute has to be written on the html element, a class or id on the body element;
for that, the tag has to be present in the source text.
Uniqueness and Order Constraints
Four elements are unique in the root structure: a document has only one html, one head,
one body, and one title. Writing a second one does not build a new element; when the
parser sees a second body tag, it does not open a new body, it merges it with the existing
one’s attributes. A document cannot have two bodies, because the tree has a single root and
that root has a single visible branch.
Order within the head section’s content is generally free; two situations are the exception.
The character-set declaration is written as early as possible — the reason for this is the
next lesson’s subject. The base element, which changes the base for relative addresses, is
also written early, because it affects addresses that come after it, and it is found only
once in the document.
The title element is the head section’s mandatory content. If it is not written, the
document keeps being processed, but it cannot be named outside its own context: the address
shows in the tab, the bookmark is saved without a name, the text introducing the document in a
search result is generated from other sources. The requirement is not a parsing condition, it
is a condition for the document to be recognizable in the outside world.
The Station Document’s Skeleton
Here is the first version of the document that will be developed throughout the course:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>North Slope Measurement Station</title> </head> <body> <h1>North Slope Measurement Station</h1> <p>An automated measurement station built at an elevation of 1840 meters above sea level.</p> </body> </html>
Five lines of declaration, two lines of content. This ratio will fall as content grows in the document; the declaration section stays fixed regardless of the document’s size.
The title element is in the head section, the h1 element is in the body, and their text is
the same here. The two do separate jobs: title names the document outside its context — in
the tab, in a bookmark, in a search result —; h1 is the topmost heading inside the document.
Because title needs to be understandable outside its context, it is often written longer in
most documents and also carries the site’s name.
Summary
- The doctype declaration is not an element; it chooses the parsing mode and carries no other information. Without it, the document is processed in quirks mode.
- The root element is
html, the document’s single root child; thelangattribute affects pronunciation, hyphenation, indexing, and typography decisions, and is redeclared where part of the document is in a different language. - The head section carries information about the document, the body carries the readable content; head-section content is not painted.
- The parser builds the
html,head, andbodyelements even when their tags are not written; the split happens at the first element that does not belong to the head section. titlenames the document outside its context,h1is the topmost heading inside the document; their text can be the same, but their functions are separate.
Next Step
This lesson built the document’s outermost shell and used tags without going into their
syntactic detail. The next lesson takes up that detail: how a tag is written, which forms of
writing attributes accept, how the < and & characters that appear in text are written, and
which error-recovery rules the parser applies to writing that breaks the rules.
To keep your progress and take notes, Log in
My notes
Log in to take notes.