Kodokon kodokon.com

Von HTML zum DOM: Parsing und Fehlerbehebung

Verstehe, wie der Browser jeden HTML-Stream, selbst einen fehlerhaften, in einen deterministischen DOM-Baum verwandelt.

10 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Anders als XML kennt HTML keinen fatalen Fehler: Die WHATWG-Spezifikation legt Byte für Byte fest, wie jeder Eingabestream - selbst ein fehlerhafter - zu einem DOM-Baum werden muss. Der Parser arbeitet in zwei Stufen: Der Tokenizer zerlegt den Stream in Tokens (Tags, Text, Kommentare), dann fügt der Tree Builder sie gemäß einer Zustandsmaschine ein, deren Zustände Insertion Modes heißen (in body, in table und so weiter). Jeder „Parse-Fehler“ hat eine standardisierte Behebung: Zwei konforme Browser erzeugen exakt dasselbe DOM.

HTML
<table>
  <tr><td>Cell</td></tr>
</table>
Das entstehende DOM enthält ein tbody, das du nie geschrieben hast.

Der Parser fügt implizite Tags ein: Ein <tbody> erscheint um die Zeilen herum, und <li>, <p> oder <option> schließen sich automatisch vor bestimmten öffnenden Tags. Noch radikaler: Jeder Inhalt, der sich in einer <table> an einer nicht erlaubten Stelle befindet, wird herausgezogen und direkt davor wieder eingefügt - ein Mechanismus namens Foster Parenting. Deine CSS-Selektoren und deine Skripte arbeiten immer auf dem korrigierten DOM, nie auf deiner Quelle.

JAVASCRIPT
const html =
  "<table><div>out</div><tr><td>ok</td></tr></table>";
const doc = new DOMParser()
  .parseFromString(html, "text/html");
console.log(doc.body.innerHTML);
// <div>out</div><table>...</table>
Foster Parenting mit DOMParser beobachtet.

Falsch verschachtelte Formatierungs-Tags lösen den Adoption Agency Algorithm aus, einen der hinterhältigsten Teile der Spezifikation: Der Parser klont die aktiven Formatierungselemente und verteilt sie neu, um einen korrekt verschachtelten Baum zu erzeugen. Das gekreuzte Markup unten zeigt das Ergebnis.

HTML
<p>
  <b>One <i>two</b> three</i>
</p>
Entstehendes DOM: <b>One <i>two</i></b><i> three</i> - das i wird in zwei Elemente aufgeteilt.

Der Parser ist ein Streaming-Parser: Er baut den Baum auf, während die Bytes eintreffen. Ein synchrones <script> hält ihn an - das Skript kann document.write aufrufen und Tokens in den Stream einschleusen -, aber der Preload Scanner durchkämmt weiterhin das restliche HTML, um Bilder, Stylesheets und Skripte vorzuladen. Wird document.write nach dem Ende des Parsings aufgerufen, löst es document.open aus und löscht die gesamte Seite: Es ist eine der ältesten Fallen der Plattform.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Was passiert mit dem <div> in <table><div>Out</div><tr><td>A</td></tr></table>?
    • Es wird stillschweigend aus dem DOM entfernt
    • Es wird durch Foster Parenting vor die Tabelle verschoben
    • Es wird in eine eigens dafür erstellte <td>-Zelle verpackt
    • Es bleibt ein direktes Kind der Tabelle
  2. Wie behandelt die HTML-Spezifikation ein fehlerhaftes Dokument?
    • Sie überlässt jedem Browser seine eigene Behebungsstrategie
    • Sie verlangt den Wechsel in den Quirks-Modus
    • Sie definiert einen deterministischen Behebungsalgorithmus, der in allen konformen Browsern identisch ist
    • Sie verlangt, dass das Parsing beim ersten Fehler stoppt
  3. Welches DOM erzeugt das Fragment <p><div>X</div></p>?
    • <p></p><div>X</div><p></p>: Das div schließt das erste p, und das verwaiste </p> erzeugt ein zweites, leeres
    • <p><div>X</div></p>, unverändert beibehalten
    • <div><p>X</p></div>, der Parser kehrt die Verschachtelung um