Table of Contents

Chapter 2. The HTML syntax

This chapter covers the syntax of HTML, i.e. how to write HTML. This chapter is similar to the Writing HTML documents section of the HTML standard.

The doctype

The doctype is required because without a doctype, browsers use quirks mode for the document, which changes some behavior, mainly in CSS. Quirks mode was introduced by IE5 for Mac, released in 2000, in an attempt to both be compatible with the contemporary legacy and with the CSS1 specification. This approach was then copied by all browsers and has now been specified. There are now three rendering modes for HTML:

Activating Browser Modes with Doctype by Henri Sivonen has more information on doctype switching. The WHATWG Quirks Mode standard specifies some of the effects of the quirks mode.

The doctype can be either:

<!doctype html>

…case-insensitive, or:

<!doctype html system "about:legacy-compat">

Also case-insensitive, except for the "about:legacy-compat" part.

The purpose of the longer doctype is for compatibility with markup generators that are unable to produce the short doctype. If you don't find yourself in such a situation, just use the short doctype.

Prior versions of HTML had other doctypes that are now defined to trigger one of the different rendering modes. For example, this HTML 4.01 doctype trigger no-quirks mode:


One of my first contributions to the WHATWG, in June 2005, was to propose to change the doctype to <!doctype html>. Finally a doctype that can be remembered! (Though, for some reason, I still remember how to type <!doctype html public "-//W3C//DTD HTML 4.01//EN" "">. Sigh.)


HTML defines the following kinds of elements:

All kinds of elements can have a start tag, although for some elements the start tag is optional.

Void elements consist of just a start tag.


The template element is special because its contents are parsed into a separate DocumentFragment instead of being children of the element itself. This is discussed in more detail in the Templates section in Chapter 3. The HTML parser.

<template><img src="[[ src ]]" alt="[[ alt ]]"></template>

Raw text elements, escapable raw text elements and normal elements have a start tag, some contents, and an end tag (but some elements have optional end tags, or optional start and end tags). Raw text means that the contents are treated as text instead of as markup, except for the end tag, and except that script has pretty special parsing rules (see Script states of the Tokenizer). Escapable raw text is like raw text, except that character references work.

<title>SpiderMonkey &amp; the GC Jitters</title>

Normal elements can have text (except < and ambiguous ampersand), character references, other elements, and comments.

The pre and textarea elements have a special rule: they may begin with a newline that will be ignored by the HTML parser. To have content that actually starts with a newline, two newlines thus have to be used. (A newline in HTML is a line feed, a carriage return, or a CRLF pair.) For example, the following is equivalent to <pre>Use the force</pre> (without a newline):

Use the force</pre>

Foreign elements are slightly closer to XML in their syntax: "/>" works (self-closing start tag), CDATA sections work (<![CDATA[ … ]]>, the contents are like raw text). But note that other aspects still work like HTML; element names and attribute names are case-insensitive, and XML namespaces don't work (only some namespaced attributes work with a predefined prefix).

<p>Circling the drain.
 <svg viewBox="-1 -1 2 2" width=16><circle r=1 /></svg>


An HTML document consists of a doctype followed by an html element, and there may be whitespace and comments before, between, and after. The following example is a complete and conforming HTML document:

<!-- a comment -->
<!doctype html>
<html lang=en>
  <title>Key to success</title>
  <p>Such like these, unless combined, are inane.</p>

Start tags

A start tag has this format:

<, the tag name (case-insensitive), whitespace (if there are attributes), any number of attributes separated by whitespace, optionally some whitespace, >. (In the HTML syntax, whitespace means ASCII whitespace, i.e., tab, line feed, form feed, carriage return, or space.)

<p class="warning">

For void elements, the tag may end with either > or />, although the slash makes no difference.


Foreign elements (SVG and MathML) support self-closing start tags, which end with /> and means there are no contents and no end tag. The element name for foreign elements is case-insensitive in the HTML syntax.

<CIRCLE r="1"/>

End tags

An end tag has this format:

</, the tag name (case-insensitive), optionally whitespace, >.


Attributes are not allowed on end tags.


Attributes come in a few different formats.

All attribute names are case-insensitive, including attributes on SVG and MathML elements.

All attribute values support character references. This can be particularly relevant for URLs in attributes, which sometimes contain & that should be escaped as &amp;, lest it be interpreted as a character reference.

<a href="?title=Lone+Surrogates&amp;reg">

If the ampersand was unescaped in this example, like this:

<a href="?title=Lone+Surrogates&reg">

...then &reg would be interpreted as a named character reference, which expands to "®", i.e., it's equivalent to:

<a href="?title=Lone+Surrogates®">

Duplicate attributes, i.e., two attributes with the same name, are not allowed.

<p class="cool" class="uncool">

Foreign elements support the following namespaced attributes (with fixed prefixes): xlink:actuate, xlink:arcrole, xlink:href, xlink:role, xlink:show, xlink:title, xlink:type, xml:lang, xml:space, xmlns (without prefix but is a namespaced attribute), xmlns:xlink.

<svg xmlns="">

Note that in the HTML syntax, it's optional to declare the namespace.


Optional tags

Certain tags can be omitted if the resulting DOM doesn't change if they are so omitted, including "minor" changes such as where whitespace ends up or where a comment ends up. The rules for when they can be omitted are slightly convoluted, but they assume that the DOM is not allowed to change by omitting a tag. It is however conforming to intentionally move a tag such that omitting it no longer changes the DOM.

For example, consider this snippet:

<p>Can a paragraph be one word long?</p>

Because there is a line feed between the paragraphs, there will be a Text node for it in the DOM. Omitting the end tags will cause the line feed to be part of the first paragraph instead:

<p>Can a paragraph be one word long?

However, in most cases this makes no difference at all. (It can make a difference if you style the paragraphs as display: inline-block, for example.)

For the exact rules on when tags can be omitted, please consult the HTML standard.

Here are the tags that may (sometimes) be omitted:

Element Start tag End tag
html Omissible Omissible
head Omissible Omissible
body Omissible Omissible
li Omissible
dt Omissible
dd Omissible
p Omissible
rt Omissible
rp Omissible
optgroup Omissible
option Omissible
colgroup Omissible Omissible
caption Omissible
thead Omissible
tbody Omissible Omissible
tfoot Omissible
tr Omissible
td Omissible
th Omissible

Character references

There are three kinds of character references:


HTML has a concept of an ambiguous ampersand, which is &, alphanumerics (a-zA-Z0-9), ;, when this is not a known named character reference. Ambiguous ampersands are not allowed. The following is an example of an ambiguous ampersand:

I've sent a support request to AT&T; no reply, yet.

However, other unescaped ampersands are technically allowed:

Ind. Unrealisk & Ind. Brunn

CDATA sections

CDATA sections can only be used in foreign content, and have this format:

<![CDATA[ (case-sensitive), text not containing ]]>, then ]]>.

<svg><title><![CDATA[ The <html>, <head>, & <title> elements ]]></title> ... </svg>


Comments have this format:

<!-- followed by any text (with some restrictions, detailed below), then -->.

<!-- Hello -->

The text is not allowed to contain --> since that would end the comment.

A somewhat recent change to comment syntax is that -- is now allowed in the text. This is not allowed in XML.

<!-- Hello -- there -->

The text is not allowed to contain <!-- since that is an indicator of a nested comment, and nested comments don't work.

<!-- <!-- this is an error --> -->

The text is not allowed to start with > or -> or contain --!> because the HTML parser will end the comment at that point.