Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Anatomie des DOM

Das DOM stellt ein XML- oder HTML-Dokument als Baum dar. Diese Seite führt in die Grundstruktur des DOM-Baums ein und beschreibt die verschiedenen Eigenschaften und Methoden, die zur Navigation verwendet werden.

Zu Beginn müssen wir einige Konzepte im Zusammenhang mit Bäumen einführen. Ein Baum ist eine Datenstruktur, die aus Knoten besteht. Jeder Knoten enthält einige Daten. Die Knoten sind hierarchisch organisiert – jeder Knoten hat einen einzelnen Elternknoten (außer dem Wurzelknoten, der keinen Elternteil hat) und eine geordnete Liste von null oder mehr Kindknoten. Nun können wir Folgendes definieren:

  • Ein Knoten ohne Elternteil wird als Wurzel des Baumes bezeichnet.
  • Ein Knoten ohne Kinder wird als Blatt bezeichnet.
  • Knoten, die denselben Elternteil haben, werden als Geschwister bezeichnet. Geschwister gehören zur selben Kindknotenliste ihres Elternteils und haben somit eine klar definierte Reihenfolge.
  • Wenn wir von Knoten A zu Knoten B gelangen können, indem wir wiederholt den Eltern-Knoten verfolgen, ist A ein Nachfahre von B und B ein Vorfahre von A.
  • Knoten in einem Baum werden in Baumreihenfolge aufgelistet, indem zuerst der Knoten selbst aufgelistet wird und dann rekursiv jeder seiner Kindknoten in Reihenfolge (Präorder-, Tiefensuche) aufgelistet wird.

Hier sind einige wichtige Eigenschaften von Bäumen:

  • Jeder Knoten ist mit einem eindeutigen Wurzelknoten verbunden.
  • Wenn Knoten A der Elternteil von Knoten B ist, dann ist Knoten B ein Kind von Knoten A.
  • Zyklen sind nicht erlaubt: Kein Knoten kann ein Vorfahre oder Nachfahre seiner selbst sein.

Das Node-Interface und seine Unterklassen

Alle Knoten im DOM werden durch Objekte dargestellt, die das Node Interface implementieren. Das Node-Interface verkörpert viele der zuvor definierten Konzepte:

  • Die parentNode Eigenschaft gibt den Elternknoten zurück oder null, wenn der Knoten keinen Elternteil hat.
  • Die childNodes Eigenschaft gibt einen NodeList der Kindknoten zurück. Die firstChild und lastChild Eigenschaften geben das erste und letzte Element dieser Liste zurück, oder null, wenn keine Kinder vorhanden sind.
  • Die getRootNode() Methode gibt die Wurzel des Baumes zurück, der den Knoten enthält, indem sie wiederholt den Elternknoten verfolgt.
  • Die hasChildNodes() Methode gibt wahr zurück, wenn es Kindknoten hat, d. h. es ist kein Blatt.
  • Die previousSibling und nextSibling Eigenschaften geben die vorherigen und nächsten Geschwisterknoten zurück oder null, wenn es kein solches Geschwister gibt.
  • Die contains() Methode gibt wahr zurück, wenn ein gegebener Knoten ein Nachfahre des Knotens ist.
  • Die compareDocumentPosition() Methode vergleicht zwei Knoten nach Baumreihenfolge. Der Abschnitt Vergleichen von Knoten bespricht diese Methode im Detail.

Sie arbeiten selten mit einfachen Node-Objekten. Stattdessen implementieren alle Objekte im DOM eines der Interfaces, die von Node erben und zusätzliche Semantik im Dokument darstellen. Die Knotentypen beschränken, welche Daten sie enthalten und welche Kindertypen gültig sind. Betrachten Sie, wie das folgende HTML-Dokument im DOM dargestellt wird:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello, world!</h1>
    <p>This is a paragraph.</p>
  </body>
</html>

Es erzeugt den folgenden DOM-Baum:

Der DOM-Baum des vorherigen HTML-Dokuments

Die Wurzel dieses DOM-Baumes ist ein Document Knoten, der das gesamte Dokument repräsentiert. Dieser Knoten wird global als die document Variable exponiert. Dieser Knoten hat zwei wichtige Kindknoten:

  • Einen optionalen DocumentType Knoten, der die doctype Deklaration darstellt. In unserem Fall gibt es einen. Dieser Knoten ist auch über die doctype Eigenschaft des Document-Knotens zugänglich.
  • Einen optionalen Element Knoten, der das Wurzelelement darstellt. Für HTML-Dokumente (wie in unserem Fall) ist dies typischerweise das HTMLHtmlElement. Für SVG-Dokumente ist dies typischerweise das SVGSVGElement. Dieser Knoten ist auch über die documentElement Eigenschaft des Document-Knotens zugänglich.

Der DocumentType-Knoten ist immer ein Blattknoten. Der Element-Knoten ist dort, wo der Hauptinhalt des Dokuments dargestellt wird. Jedes darunterliegende Element, wie zum Beispiel <head>, <body> und <p>, wird ebenfalls durch einen Element-Knoten repräsentiert. Tatsächlich ist jeder eine Unterklasse von Element, die spezifisch für diesen Tag-Namen ist, wie im HTML-Spezifikation definiert, wie HTMLHeadElement und HTMLBodyElement, mit zusätzlichen Eigenschaften und Methoden, die die Semantik dieses Elements darstellen, aber hier konzentrieren wir uns auf die gemeinsamen Verhaltensweisen des DOM. Die Element-Knoten können andere Element-Knoten als Kinder haben, die verschachtelte Elemente darstellen. Zum Beispiel hat das <head>-Element drei Kinder: zwei <meta>-Elemente und ein <title>-Element. Zusätzlich können Elemente auch Text Knoten und CDATASection Knoten als Kinder haben, die Textinhalte darstellen. Zum Beispiel hat das <p>-Element ein einzelnes Kind, einen Text-Knoten, der die Zeichenkette "Dies ist ein Absatz." enthält. Text-Knoten und CDATASection-Knoten sind immer Blattknoten.

Alle Knoten, die Kinder haben können (Document, DocumentFragment und Element), erlauben zwei Arten von Kindern: Comment und ProcessingInstruction Knoten. Diese Knoten sind immer Blattknoten.

Jedes Element kann, zusätzlich zu den Kindknoten, auch Attribute haben, die als Attr Knoten dargestellt werden. Attr erweitert das Node-Interface, aber sie sind nicht Teil der Hauptbaumstruktur, da sie kein Kind eines Knotens sind und ihr Elternknoten null ist. Stattdessen werden sie in einem separaten benannten Knoten-Map gespeichert, die über die attributes Eigenschaft des Element-Knotens zugänglich ist.

Das Node-Interface definiert eine nodeType Eigenschaft, die den Typ des Knotens angibt. Zusammengefasst haben wir die folgenden Knotentypen eingeführt:

Knotentyp nodeType-Wert Gültige Kinder (außer Comment und ProcessingInstruction)
Document Node.DOCUMENT_NODE (9) DocumentType, Element
DocumentType Node.DOCUMENT_TYPE_NODE (10) Keine
Element Node.ELEMENT_NODE (1) Element, Text, CDATASection
Text Node.TEXT_NODE (3) Keine
CDATASection Node.CDATA_SECTION_NODE (4) Keine
Comment Node.COMMENT_NODE (8) Keine
ProcessingInstruction Node.PROCESSING_INSTRUCTION_NODE (7) Keine
Attr Node.ATTRIBUTE_NODE (2) Keine

Hinweis: Sie werden bemerken, dass wir hier einige Knotentypen übersprungen haben. Die Node.ENTITY_REFERENCE_NODE (5), Node.ENTITY_NODE (6) und Node.NOTATION_NODE (12) Werte werden nicht mehr verwendet, während der Node.DOCUMENT_FRAGMENT_NODE (11) Wert im Erstellen und Aktualisieren des DOM-Baums eingeführt wird.

Daten jedes Knotens

Jeder Knotentyp hat seine eigene Art, die Daten darzustellen, die er hält. Die Node-Schnittstelle selbst definiert drei Eigenschaften, die mit Daten zu tun haben, die in der folgenden Tabelle zusammengefasst sind:

Knotentyp nodeName nodeValue textContent
Document "#document" null null
DocumentType Sein name (z.B., "html") null null
Element Sein tagName (z.B., "HTML", "BODY") null Verkettung aller seiner Textknoten-Nachfahren in Baumreihenfolge
Text "#text" Sein data Sein data
CDATASection "#cdata-section" Sein data Sein data
Comment "#comment" Sein data Sein data
ProcessingInstruction Sein target Sein data Sein data
Attr Sein name Sein value Sein value

Dokument

Der Document-Knoten enthält selbst keine Daten, sodass seine nodeValue und textContent immer null sind. Sein nodeName ist immer "#document".

Das Document definiert einige Metadaten über das Dokument, die aus der Umgebung stammen (zum Beispiel die HTTP-Antwort, die das Dokument ausgeliefert hat):

  • Die URL und documentURI Eigenschaften geben die URL des Dokuments zurück.
  • Die characterSet Eigenschaft gibt die im Dokument verwendete Zeichenkodierung zurück, wie zum Beispiel "UTF-8".
  • Die compatMode Eigenschaft gibt den Darstellungsmodus des Dokuments zurück, entweder "CSS1Compat" (Standardmodus) oder "BackCompat" (Quirks-Modus).
  • Die contentType Eigenschaft gibt den Medientyp des Dokuments zurück, wie zum Beispiel "text/html" für HTML-Dokumente.

DocumentType

Ein DocumentType im Dokument sieht so aus:

xml
<!doctype name PUBLIC "publicId" "systemId">

Es gibt drei Teile, die Sie angeben können, die den drei Eigenschaften des DocumentType-Knotens entsprechen: name, publicId und systemId. Für HTML-Dokumente ist der Doctype immer <!doctype html>, daher ist der name "html" und sowohl publicId als auch systemId sind leere Zeichenfolgen.

Element

Ein Element im Dokument sieht so aus:

html
<p class="note" id="intro">This is a paragraph.</p>

Zusätzlich zu den Inhalten gibt es zwei Teile, die Sie angeben können: den Tag-Namen und die Attribute. Der Tag-Name entspricht der tagName Eigenschaft des Element-Knotens, der in diesem Fall "P" ist (beachten Sie, dass er bei HTML-Elementen immer großgeschrieben ist). Die Attribute entsprechen den Attr-Knoten, die in der attributes Eigenschaft des Element-Knotens gespeichert sind. Wir werden Attribute im Abschnitt Das Element und seine Attribute ausführlicher besprechen.

Der Element-Knoten enthält selbst keine Daten, daher ist seine nodeValue immer null. Sein textContent ist die Verkettung aller seiner Textknoten-Nachfahren in Baumreihenfolge, die in diesem Fall "Dies ist ein Paragraph." ist. Für das folgende Element:

html
<div>Hello, <span>world</span>!</div>

ist das textContent "Hello, world!", wobei der Textknoten "Hello, ", der Textknoten "world" im <span>-Element und der Textknoten "!" miteinander verknüpft werden.

CharacterData

Text, CDATASection, Comment, und ProcessingInstruction erben alle von der CharacterData Schnittstelle, die eine Unterklasse von Node ist. Die CharacterData Schnittstelle definiert eine einzelne Eigenschaft, data, die den Textinhalt des Knotens enthält. Die data Eigenschaft wird auch verwendet, um die nodeValue und textContent Eigenschaften dieser Knoten zu implementieren.

Für Text und CDATASection hält die data Eigenschaft den Textinhalt des Knotens. Im folgenden Dokument (beachten Sie, dass wir ein SVG-Dokument verwenden, da HTML keine CDATA-Abschnitte erlaubt):

svg
<text>Some text</text>
<style><![CDATA[h1 { color: red; }]]></style>

hat der Textknoten im <text>-Element "Some text" als data, und der CDATA-Abschnitt im <style>-Element hat "h1 { color: red; }" als data.

Für Comment hält die data Eigenschaft den Inhalt des Kommentars, der nach dem <!-- beginnt und vor dem --> endet. Zum Beispiel im folgenden Dokument:

html
<!-- This is a comment -->

hat der Kommentar-Knoten " Dies ist ein Kommentar " als data.

Für ProcessingInstruction hält die data Eigenschaft den Inhalt der Verarbeitungseinweisung, die nach dem Ziel beginnt und vor dem ?> endet. Zum Beispiel im folgenden Dokument:

xml
<?xml-stylesheet type="text/xsl" href="style.xsl"?>

hat der Verarbeitungseinweisung-Knoten 'type="text/xsl" href="style.xsl"' als data und "xml-stylesheet" als sein target.

Zusätzlich definiert die CharacterData Schnittstelle die length Eigenschaft, die die Länge der data Zeichenfolge zurückgibt, und die substringData() Methode, die einen Unterstring der data zurückgibt.

Attr

Für das folgende Element:

html
<p class="note" id="intro">This is a paragraph.</p>

hat das <p> Element zwei Attribute, die durch zwei Attr-Knoten dargestellt werden. Jedes Attribut besteht aus einem Namen und einem Wert, die den name und value Eigenschaften entsprechen. Das erste Attribut hat "class" als name und "note" als value, während das zweite Attribut "id" als name und "intro" als value hat.

Das Element und seine Attribute

Wie bereits erwähnt, werden die Attribute eines Element-Knotens durch Attr-Knoten dargestellt, die in einem separaten benannten Knoten-Map gespeichert werden, das über die attributes Eigenschaft des Element-Knotens zugänglich ist. Diese NamedNodeMap Schnittstelle definiert drei wichtige Eigenschaften:

  • length, die die Anzahl der Attribute zurückgibt.
  • item() Methode, die das Attr an einem gegebenen Index zurückgibt.
  • getNamedItem() Methode, die das Attr mit einem bestimmten Namen zurückgibt.

Das Element-Interface definiert außerdem mehrere Methoden, um direkt mit Attributen zu arbeiten, ohne auf den benannten Knoten-Map zugreifen zu müssen:

Sie können auch über die ownerElement Eigenschaft des Attr-Knotens auf das Eigentümer-Element eines Attributs zugreifen.

Es gibt zwei spezielle Attribute, id und class, die ihre eigenen Eigenschaften im Element-Interface haben: id und className, die den Wert des entsprechenden Attributs reflektieren. Zusätzlich gibt die classList Eigenschaft eine DOMTokenList zurück, die die Liste der Klassen im class-Attribut darstellt.

Arbeiten mit dem Elementbaum

Da Element-Knoten das Rückgrat der Dokumentstruktur bilden, können Sie speziell die Elementknoten durchlaufen und andere Knoten (wie Text und Comment) überspringen.

  • Für alle Knoten gibt die parentElement Eigenschaft den Elternknoten zurück, wenn es sich um ein Element handelt, oder null, wenn der Elternteil kein Element ist (zum Beispiel, wenn der Elternteil ein Document ist). Dies steht im Gegensatz zu parentNode, das den Elternknoten unabhängig von seinem Typ zurückgibt.
  • Für Document, DocumentFragment und Element gibt die children Eigenschaft eine HTMLCollection von nur den Kind-Element-Knoten zurück. Dies steht im Gegensatz zu childNodes, das alle Kindknoten zurückgibt. Die firstElementChild und lastElementChild Eigenschaften geben das erste und letzte Element dieser Sammlung zurück oder null, wenn keine Kind-Elemente vorhanden sind. Die childElementCount Eigenschaft gibt die Anzahl der Kind-Elemente zurück.
  • Für Element und CharacterData geben die previousElementSibling und nextElementSibling Eigenschaften das vorherige und nächste Geschwisterelement zurück, das ein Element ist, bzw. null, wenn kein solches Geschwisterelement existiert. Dies steht im Gegensatz zu previousSibling und nextSibling, die jeden Typ von Geschwisterknoten zurückgeben können.

Vergleichen von Knoten

Es gibt drei wichtige Methoden, die Knoten vergleichen: isEqualNode(), isSameNode(), compareDocumentPosition().

Die isSameNode() Methode ist veraltet. Jetzt verhält sie sich wie der strikte Gleichheitsoperator (===), indem sie wahr zurückgibt, wenn und nur wenn die beiden Knoten dasselbe Objekt sind.

Die isEqualNode() Methode vergleicht zwei Knoten strukturell. Zwei Knoten werden als gleich angesehen, wenn sie denselben Typ, dieselben Daten haben und ihre Kindknoten an jedem Index ebenfalls gleich sind. Im Abschnitt Daten jedes Knotens haben wir bereits die Daten definiert, die für jeden Knotentyp relevant sind:

  • Für Document gibt es keine Daten, daher müssen nur die Kindknoten verglichen werden.
  • Für DocumentType müssen die Eigenschaften name, publicId und systemId verglichen werden.
  • Für Element müssen tagName (genauer gesagt, namespaceURI, prefix und localName; wir werden diese im XML-Namensräume Leitfaden einführen) und die Attribute verglichen werden.
  • Für Attr müssen die Eigenschaften name (genauer gesagt, namespaceURI, prefix und localName; wir werden diese im XML-Namensräume Leitfaden einführen) und value verglichen werden.
  • Für alle CharacterData Knoten (Text, CDATASection, Comment und ProcessingInstruction) muss die data Eigenschaft verglichen werden. Für ProcessingInstruction muss auch die target Eigenschaft verglichen werden.

Die a.compareDocumentPosition(b) Methode vergleicht zwei Knoten nach Baumreihenfolge. Sie gibt eine Bitmaske zurück, die ihre relativen Positionen anzeigt. Die möglichen Fälle sind:

  • Gibt 0 zurück, wenn a und b derselbe Knoten sind.
  • Wenn die beiden Knoten beide Attribute desselben Elementknotens sind, gibt es Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC (34) zurück, wenn a b in der Attributliste vorausgeht, oder Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC (36), wenn a b folgt. Wenn einer der beiden Knoten ein Attribut ist, wird das Eigentümerelement für weitere Vergleiche verwendet.
  • Wenn die beiden Knoten nicht denselben Wurzelknoten haben, gibt es entweder Node.DOCUMENT_POSITION_DISCONNECTED | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_PRECEDING (35) oder Node.DOCUMENT_POSITION_DISCONNECTED | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_FOLLOWING (37) zurück. Welcher zurückgegeben wird, ist implementierungsspezifisch.
  • Wenn a ein Vorfahre von b ist (einschließlich, wenn b ein Attribut von a ist), gibt es Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING (10) zurück.
  • Wenn a ein Nachfahre von b ist (einschließlich, wenn a ein Attribut von b ist), gibt es Node.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_FOLLOWING (20) zurück.
  • Wenn a b in Baumreihenfolge vorausgeht, gibt es Node.DOCUMENT_POSITION_PRECEDING (2) zurück.
  • Wenn a b in Baumreihenfolge folgt, gibt es Node.DOCUMENT_POSITION_FOLLOWING (4) zurück.

Bitmaskenwerte werden verwendet, daher können Sie eine bitweise UND-Operation verwenden, um spezifische Beziehungen zu überprüfen. Zum Beispiel, um zu überprüfen, ob a b vorausgeht, können Sie:

js
if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING) {
  // a precedes b
}

Dies berücksichtigt die Fälle, in denen a und b Attribute desselben Elements sind, a ein Vorfahre von b ist und a b in Baumreihenfolge vorausgeht.

Zusammenfassung

Hier sind alle Funktionen, die wir bisher eingeführt haben. Es sind viele, aber sie sind alle in verschiedenen Szenarien nützlich.