XML Mode vs HTML Mode
Relevant source files
The following files were used as context for generating this wiki page:
In htmlparser2, the xmlMode option is the primary toggle that determines how the parser and tokenizer interpret document syntax. While the project is a "forgiving" parser, switching between these modes significantly alters behaviors related to tag casing, entity decoding, self-closing tags, and the handling of special elements like CDATA and processing instructions.
HTML mode is the default state, designed for standard web documents, whereas XML mode is recommended for RSS, Atom, and RDF feeds where strict adherence to structured data formats is required.
Sources: README.md:104-108, src/Parser.ts:167-172
Architectural Impact of xmlMode
The xmlMode option is propagated from the Parser constructor to the Tokenizer. It acts as a master switch that sets defaults for several other parser options unless they are explicitly overridden.
The diagram shows how the xmlMode flag cascades into specific behavior flags within the Parser and Tokenizer.
Sources: src/Parser.ts:245-258, src/Tokenizer.ts:178-185
Core Configuration Differences
| Feature | HTML Mode (xmlMode: false) | XML Mode (xmlMode: true) |
|---|---|---|
| Tag Casing | Lowercased by default | Preserved as-is |
| Attribute Casing | Lowercased by default | Preserved as-is |
| Self-closing Tags | Only for voidElements (e.g., <br>) | Recognized for all tags |
| CDATA Sections | Often treated as comments | Recognized as text content |
| Entities | Decoded using HTML entities tree | Decoded using XML entities tree |
| Special Tags | <script>, <style>, <title> parsed as raw text | All tags treated equally |
Sources: README.md:113-118, src/Parser.ts:182-209, src/Tokenizer.ts:185-188
Tag and Element Handling
Void and Self-Closing Elements
In HTML mode, the parser maintains a hardcoded set of voidElements (such as area, br, img, input) that are not expected to have children. Any slash / in these tags is largely ignored unless recognizeSelfClosing is enabled. In XML mode, any tag can be self-closing (e.g., <entry/>), which immediately triggers an onclosetag event.
Sources: src/Parser.ts:79-99, src/Parser.ts:311-313, src/Tokenizer.ts:544-555
Case Sensitivity and Adjustments
HTML mode generally forces tag names to lowercase. However, htmlparser2 implements "Foreign Context" awareness for SVG and MathML. When the parser detects an <svg> or <math> tag in HTML mode, it enters a temporary state where it applies specific case adjustments (e.g., altglyph becomes altGlyph) to match the expected DOM casing. In pure XML mode, casing is strictly preserved without these adjustments.
Sources: src/Parser.ts:114-152, src/Parser.ts:326-348
Tokenizer State Logic
The Tokenizer uses the xmlMode flag to branch logic in several critical states, particularly for declarations and processing instructions.
Logic flow within the Tokenizer showing how HTML mode treats unknown declarations and processing instructions as "special comments".
Sources: src/Tokenizer.ts:602-618, src/Tokenizer.ts:503-512
Processing Instructions and Declarations
In XML mode, processing instructions (syntax <?...?>) are explicitly emitted via onprocessinginstruction. In HTML mode, <? is diverted to a "bogus comment" state, where it is treated as a comment until the next >. Similarly, non-doctype declarations (syntax <!...) are treated as bogus comments in HTML mode but as standard declarations in XML mode.
Sources: src/Tokenizer.ts:646-658, src/Tokenizer.ts:602-610, src/Parser.ts:466-479
Entity Decoding
The project utilizes different decoding trees based on the mode.
- XML Mode: Uses
xmlDecodeTree. It is stricter and typically only decodes the five standard XML entities (&,<,>,",') and numeric entities. - HTML Mode: Uses
htmlDecodeTree, which includes a vast library of named entities and supports "legacy" entities (entities not ending in a semicolon, like©).
Sources: src/Tokenizer.ts:186, src/Tokenizer.ts:684-694, src/Parser.events.spec.ts:121-131
CDATA Handling
CDATA sections (<![CDATA[ ... ]]>) are handled differently based on context:
- XML Mode: Always recognized; triggers
oncdatastart,ontext, andoncdataend. - HTML Mode (Default): Treated as a comment:
<!--[CDATA[ ... ]]>-->. - HTML Mode (Foreign Context): Inside
<svg>or<math>, CDATA is recognized as text content even ifxmlModeis false.
Sources: src/Parser.ts:495-508, src/Tokenizer.ts:771-789, src/index.spec.ts:30-33
Logic of Implicit Closing Tags
HTML mode implements a complex mapping of tags that implicitly close other tags (e.g., a <li> closes a previous <li>, a <tr> closes a <td>). This logic is entirely bypassed in XML mode, where every tag must be explicitly closed or self-closed.
// src/Parser.ts:16-56
const openImpliesClose = new Map<string, Set<string>>([
["tr", new Set(["tr", "th", "td"])],
["th", new Set(["th", "td"])],
["li", new Set(["li"])],
["p", new Set(["p"])],
// ... many more mappings
]);
Sources: src/Parser.ts:16-75, src/Parser.ts:366-371
Conclusion
The distinction between XML and HTML modes in htmlparser2 is fundamental to its "forgiving" nature. HTML mode provides the necessary heuristics to parse real-world web content (implicit tags, legacy entities, raw-text blocks), while XML mode provides the strictness required for data exchange formats. Developers should select xmlMode: true specifically when dealing with non-HTML structured data to ensure case preservation and correct processing of instructions.
Sources: README.md:10-12, README.md:160-165