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 odernull
, wenn der Knoten keinen Elternteil hat. - Die
childNodes
Eigenschaft gibt einenNodeList
der Kindknoten zurück. DiefirstChild
undlastChild
Eigenschaften geben das erste und letzte Element dieser Liste zurück, odernull
, 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 gibtwahr
zurück, wenn es Kindknoten hat, d. h. es ist kein Blatt. - Die
previousSibling
undnextSibling
Eigenschaften geben die vorherigen und nächsten Geschwisterknoten zurück odernull
, wenn es kein solches Geschwister gibt. - Die
contains()
Methode gibtwahr
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:
<!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:
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 diedoctype
Eigenschaft desDocument
-Knotens zugänglich. - Einen optionalen
Element
Knoten, der das Wurzelelement darstellt. Für HTML-Dokumente (wie in unserem Fall) ist dies typischerweise dasHTMLHtmlElement
. Für SVG-Dokumente ist dies typischerweise dasSVGSVGElement
. Dieser Knoten ist auch über diedocumentElement
Eigenschaft desDocument
-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
unddocumentURI
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:
<!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:
<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:
<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):
<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:
<!-- 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-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:
<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 dasAttr
an einem gegebenen Index zurückgibt.getNamedItem()
Methode, die dasAttr
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:
element.getAttribute(name)
ist äquivalent zuelement.attributes.getNamedItem(name).value
, wenn das Attribut existiert.element.getAttributeNode(name)
ist äquivalent zuelement.attributes.getNamedItem(name)
.element.hasAttribute(name)
ist äquivalent zuelement.attributes.getNamedItem(name) !== null
.element.getAttributeNames()
gibt ein Array aller Attributnamen zurück.element.hasAttributes()
ist äquivalent zuelement.attributes.length > 0
.
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 einElement
handelt, odernull
, wenn der Elternteil keinElement
ist (zum Beispiel, wenn der Elternteil einDocument
ist). Dies steht im Gegensatz zuparentNode
, das den Elternknoten unabhängig von seinem Typ zurückgibt. - Für
Document
,DocumentFragment
undElement
gibt diechildren
Eigenschaft eineHTMLCollection
von nur den Kind-Element
-Knoten zurück. Dies steht im Gegensatz zuchildNodes
, das alle Kindknoten zurückgibt. DiefirstElementChild
undlastElementChild
Eigenschaften geben das erste und letzte Element dieser Sammlung zurück odernull
, wenn keine Kind-Elemente vorhanden sind. DiechildElementCount
Eigenschaft gibt die Anzahl der Kind-Elemente zurück. - Für
Element
undCharacterData
geben diepreviousElementSibling
undnextElementSibling
Eigenschaften das vorherige und nächste Geschwisterelement zurück, das einElement
ist, bzw.null
, wenn kein solches Geschwisterelement existiert. Dies steht im Gegensatz zupreviousSibling
undnextSibling
, 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 Eigenschaftenname
,publicId
undsystemId
verglichen werden. - Für
Element
müssentagName
(genauer gesagt,namespaceURI
,prefix
undlocalName
; wir werden diese im XML-Namensräume Leitfaden einführen) und die Attribute verglichen werden. - Für
Attr
müssen die Eigenschaftenname
(genauer gesagt,namespaceURI
,prefix
undlocalName
; wir werden diese im XML-Namensräume Leitfaden einführen) undvalue
verglichen werden. - Für alle
CharacterData
Knoten (Text
,CDATASection
,Comment
undProcessingInstruction
) muss diedata
Eigenschaft verglichen werden. FürProcessingInstruction
muss auch dietarget
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, wenna
undb
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, wenna
b
in der Attributliste vorausgeht, oderNode.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
(36), wenna
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) oderNode.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 vonb
ist (einschließlich, wennb
ein Attribut vona
ist), gibt esNode.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING
(10) zurück. - Wenn
a
ein Nachfahre vonb
ist (einschließlich, wenna
ein Attribut vonb
ist), gibt esNode.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_FOLLOWING
(20) zurück. - Wenn
a
b
in Baumreihenfolge vorausgeht, gibt esNode.DOCUMENT_POSITION_PRECEDING
(2) zurück. - Wenn
a
b
in Baumreihenfolge folgt, gibt esNode.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:
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.
- Alle Knoten im DOM implementieren das
Node
Interface. - Um im DOM-Baum zu navigieren:
parentNode
,childNodes
,firstChild
/lastChild
,hasChildNodes()
,getRootNode()
,previousSibling
/nextSibling
. - Um im Elementbaum zu navigieren:
parentElement
,children
,firstElementChild
/lastElementChild
,childElementCount
,previousElementSibling
/nextElementSibling
. - Die
nodeType
Eigenschaft gibt den Typ des Knotens an. DienodeName
,nodeValue
undtextContent
Eigenschaften liefern die von dem Knoten gehaltenen Daten. - Der
Document
Knoten und seine zwei wichtigen Kinder:doctype
unddocumentElement
. - Der
DocumentType
Knoten und seine drei Eigenschaften:name
,publicId
undsystemId
. - Der
Element
Knoten und seine Eigenschaften:tagName
,attributes
. - Der
Attr
Knoten und seine Eigenschaften:name
undvalue
. - Das
CharacterData
Interface und seine Eigenschaft:data
. - Die vier
CharacterData
Unterklassen:Text
,CDATASection
,Comment
undProcessingInstruction
.ProcessingInstruction
hat auch dietarget
Eigenschaft. - Die verschiedenen Möglichkeiten, mit Attributen zu arbeiten, einschließlich der
id
,className
undclassList
Eigenschaften. - Die drei Methoden zum Vergleichen von Knoten:
isEqualNode()
,isSameNode()
undcompareDocumentPosition()
.