Startseite / Artikel

DOM-Node sind nicht der HTML-String, den Sie übermittelt haben.

Der DOM ist nicht der HTML-String, den Sie übermittelt haben. Parse-Bäume, laufende NodeLists und Reflow-Effekte erklären, warum ein Denken auf String-Ebene in JavaScript- und React-Frontends zum Scheitern führt.

Verlag: REACTAPP.TOP5 Min. Lesezeit1180 Wörter

Quellen: The DOM Is Not Your HTML: The Difference Every Frontend Developer Should Understand

Man erstellt HTML, doch JavaScript ändert diese Datei auf dem Datenträger niemals. Es kommuniziert mit dem laufenden Document Object Model des Browsers.

Wenn die Markup-Datei zum Browser gesendet wird, entsteht die anfängliche Seitenstruktur. Eine Seite kann mit einem einfachen Überschriftstext beginnen:

<h1>Hello</h1>

Nach dem Laden kann JavaScript diese Überschrift finden und ihren Text ersetzen:

document.querySelector("h1").textContent = "Hello World";

Dann wird Folgendes angezeigt:

<h1>Hello World</h1>

Es mag so aussehen, als wäre die HTML-Datei selbst umgeschrieben worden. Das ist jedoch nicht der Fall. Die Datei auf dem Server enthält weiterhin die ursprüngliche Überschrift. JavaScript hat lediglich das laufende Modell der Seite im Browser – das DOM – geändert. Die Trennung des Quell-Markups von diesem laufenden Modell macht viele Verhaltensweisen von JavaScript klarer.

HTML ist eine Dokumentdatei. Das DOM ist eine laufende Darstellung.

HTML ist das Markup, das man selbst erstellt, zum Beispiel:

<body>
  <h1>Hello</h1>
  <button>Click me</button>
</body>

Der Browser analysiert dieses Dokument zu einer in Erinnerung gespeicherten Struktur. Diese Struktur ist das Document Object Model. Ein grober Überblick über den Ablauf sieht so aus:

HTML
 │
 │ browser parses
 ▼
DOM
 │
 ├── body
 │    ├── h1
 │    └── button

Das DOM ist nicht einfach nur eine Zeichenkettenversion Ihres HTMLs. Es handelt sich um ein strukturiertes, programmierbares Dokument. Genau das manipuliert JavaScript.

Was ändert JavaScript also tatsächlich?

Starten Sie mit:

<h1 id="title">Hello</h1>

Dann führen Sie aus:

const title = document.querySelector("#title");
title.textContent = "Welcome";

Die Skript findet den DOM-Node und aktualisiert seinen Text. Der aktuelle Baum repräsentiert nun:

<h1 id="title">Welcome</h1>

Die ursprüngliche HTML-Quelle wurde nicht umgeschrieben. Wenn die Markierung von einem Server oder einer statischen Datei stammt, enthält diese Quelle weiterhin:

<h1 id="title">Hello</h1>

Der Unterschied ist gering, aber entscheidend: Die Änderung des DOMs ist nicht dasselbe wie die Bearbeitung der HTML-Quelle.

So können Sie den Unterschied leicht erkennen

Öffnen Sie eine Seite und ändern Sie etwas über die Konsole:

document.querySelector("h1").textContent = "Changed!";

Die sichtbare Seite aktualisiert sich sofort. Vergleichen Sie den laufenden DOM:

<h1>Changed!</h1>

mit der ursprünglichen HTML-Quelle:

<h1>Hello</h1>

Sie können auseinanderdriften, weil der Browser mit seinem laufenden DOM arbeitet.

Der DOM ist mehr als das, was Sie ursprünglich geschrieben haben

Nehmen wir an, die Markup-Struktur beginnt wie folgt:

<ul id="users">
  <li>John</li>
</ul>

JavaScript kann ein weiteres Element hinzufügen:

const list = document.querySelector("#users");
const item = document.createElement("li");
item.textContent = "Sarah";
list.appendChild(item);

Der laufende Baum enthält nun:

ul
├── li "John"
└── li "Sarah"

„Sarah“ erschien nie in der ursprünglichen Datei. Der Browser erfordert kein umgeschriebenes Quelldokument; das Skript hat den laufenden DOM verändert. Diese Fähigkeit bildet die Grundlage interaktiver Websites.

Der DOM ist ein Baum

„Object Model“ klingt komplizierter, als es in Wirklichkeit ist. Im Grunde genommen ist der DOM ein Baum aus Knoten. Gegeben:

<main>
  <h1>Hello</h1>
  <p>Welcome!</p>
</main>

baut der Browser eine Struktur ungefähr wie folgt auf:

Document
   │
   └── main
       ├── h1
       │    └── "Hello"
       │
       └── p
            └── "Welcome!"

Diese Baumstruktur ist der Grund, warum Skripte Beziehungen wie diese verfolgen können:

element.parentElement
element.children
element.nextElementSibling

Man scannt keine einfache Textdatei, sondern navigiert in einem strukturierten Baum.

Und der DOM kann sich ändern

Die Quelldatei in HTML ist in der Regel das ursprüngliche Dokument. Der im Speicher vorhandene Baum bleibt während der Laufzeit veränderbar. Skripte können Knoten zuweisen:

const button = document.createElement("button");

Attribute setzen:

button.setAttribute("disabled", "");

Inhalt ändern:

button.textContent = "Loading...";

und den Knoten einfügen:

document.body.append(button);

Der Baum entwickelt sich während des Laufs der Anwendung weiter, weshalb interaktive Benutzeroberflächen für jede kleine Änderung keinen vollständigen HTML-Download benötigen.

Aber es gibt noch eine Sache, die Entwickler oft verwechseln

Durch das Inspektieren einer Seite kann HTML sichtbar werden, das ursprünglich nie in der Quelldatei enthalten war. Skripte können folgendes erstellen:

<div class="notification">
  Saved successfully
</div>

Vous sind nicht der Autor dieses Nodes, doch DevTools können ihn im Elements-Panel anzeigen, weil dieses Panel den DOM widerspiegelt. „View Source“ kann den Node möglicherweise ganz weglassen.

Daher ist nicht garantiert, dass „View Source“ und „Inspect Element“ übereinstimmen. Ein häufiger Grund ist, dass JavaScript den DOM nach dem initialen HTML-Laden verändert.

Warum ist das für Frontend-Entwickler wichtig?

Viel vom Verhalten der Benutzeroberfläche wird klarer, sobald sich das mentale Modell von „JavaScript ändert die HTML-Struktur“ auf „JavaScript ändert den DOM“ verschiebt. Diese Sichtweise erklärt APIs wie:

element.classList.add("active");
element.remove();
element.textContent = "Loading...";
element.append(newElement);

Diese Aufrufe arbeiten mit der aktuellen Dokumentstruktur. Der Browser verwendet dann diesen aktualisierten Baum beim Darstellen des Bildschirms.

Wie sieht es mit React aus?

Derselbe Unterschied hilft auch beim Erlernen von React. Man könnte schreiben:

return <h1>Hello</h1>;

JSX ist nicht wörtlich das DOM. Es handelt sich um eine Syntax, die die Benutzeroberfläche beschreibt. React entscheidet, wie diese Beschreibung im echten DOM widergespiegelt werden soll. Das Verständnis des DOMs vor dem Eintauchen in die Abstraktionen von Frameworks macht Rendering, Reconciliation, Refs und DOM-Updates weniger rätselhaft.

Ihnen sind nicht alle internen Details notwendig. Behalten Sie diesen Ablauf im Hinterkopf:

HTML
  ↓
Browser parses it
  ↓
DOM
  ↓
JavaScript can modify it
  ↓
Browser updates the rendered page

Frameworke fügen zusätzliche Schichten darauf auf, doch das Browser-DOM bleibt die Grundlage.

Ein häufiger Fehler

Entwickler betrachten manchmal eine DOM-Veränderung als dauerhafte Änderung am ursprünglichen HTML. Das ist jedoch nicht der Fall. Zum Beispiel:

document.body.innerHTML = "<h1>New page</h1>";

Diese Zuweisung kann die aktuelle Live-Struktur ersetzen, schreibt jedoch die HTML-Datei auf dem Server nicht um. Ein Neuladen lädt das Dokument aus seiner Quelle neu. Sofern die Änderung nicht gespeichert und kein neues HTML erzeugt wurde, kehrt das ursprüngliche Dokument zurück. Diese Lücke trennt den Laufzeitzustand vom Quellinhalt.

Das zu merkende mentale Modell

Vermeiden Sie es, die Konzepte miteinander zu vermischen:

HTML = DOM

Wählen Sie einen Ablauf, der eher dem folgt:

HTML
 │
 │ parsed by browser
 ▼
DOM
 │
 │ modified by JavaScript
 ▼
Updated DOM
 │
 │ used during rendering
 ▼
What the user sees

Betrachten Sie HTML als das ursprüngliche, von Menschen verfasste Dokument. Betrachten Sie den DOM als den vom Browser aus diesem Quelltext erstellten, veränderbaren Baum. Skripte ändern diesen Baum; die Darstellung folgt dem, was der Baum aktuell enthält.

Zusammenfassung

Vergleichen Sie den DOM nicht mit der HTML-Datei. Das Markup ist das, was Sie selbst erstellen; das Objektmodell hingegen wird vom Browser beibehalten, solange die Registerkarte geöffnet ist. Durch diese Trennung werden mehrere Aspekte klar: Warum Skripte Knoten hinzufügen und entfernen können, warum querySelector() funktioniert, warum textContent das anzeigt, was man sieht, warum DevTools Knoten anzeigen können, die Sie nie erstellt haben, warum der Live-Tree von der Quelle abweichen kann und warum React letztendlich dennoch auf den DOM zugreift.

Der nützliche Ansatz ist einfach: Sie schreiben HTML, der Browser erstellt den DOM und JavaScript ändert den DOM. Sobald das verstanden ist, wirkt der DOM nicht mehr wie eine undurchsichtige API, sondern zeigt sich so, wie er wirklich ist – als lebendige Darstellung der Seite durch den Browser.