Nodes (Knoten)
Die Elemente des DOM werden als Knoten (Nodes) bezeichnet. Nicht nur HTML-Tags bilden Knoten, sondern HTML-Attribute, Inhalte und Kommentare sind ebenfalls Nodes.
Gemäß dem W3C HTML DOM-Standard, ist alles in einem HTML-Dokument ein Knoten.
- Das HTML-Dokument ist ein document node
- Jedes HTML-Element (Tag) ist ein element node
- Der Text im Element ist ein text node
- Jedes HTML-Attribut ist ein attribute node
- Kommentare sind comment nodes
- Whitespace ist ein text node
Node Beziehungen
Knoten in einer Baumstruktur haben hierarchische Beziehungen zueinander.
Die Begriffe parent, child, und sibling beschreiben den Beziehungs-Typ.
- Jeder Knoten hat genau ein parent , außer dem Wurzelelement
- Jeder Knoten kann unterschiedlich viele children haben
- Siblings (Geschwister) sind Knoten mit denselben parent
Beziehunstypen in diesem Beispiel
- body ist ein Kind von html
- div ist ein Kind von body
- p und a sind Kinder von div und somit Geschwister
- p besitzt vier Kinder, einen Kommentar ein strong-Element und zwei mal Text (diese Kinder sind Geschwister)
- strong besitzt ein Kind: Text
- Attribute sind keine Kinder sondern Eigenschaften eines Elements
<html>
<body>
<div>
<p>
<!--Das ist ein Kommentar-->
Absatz Text <strong>fetter Text</strong> Absatz Text
</p>
<a href="#">Link Text</a>
</div>
</body>
</html>
Laut W3-Konsortium sind Attribute hierarchisch gesehen keine Unterobjekte von Elementen, sondern assoziierte Objekte.
Um auf Attributknoten zuzugreifen, bietet das node-Objekt eigene Eigenschaften und Methoden an.
Hinweis zum Script
Ich habe mich in diesem Script zur folgenden Logik entschieden, um Verwirrungen nicht noch Größer zu machen!
In JavaScript ist alles ein Node, wenn von Elementen gesprochen wird ist ein HTML-Element gemeint (z.B. DIV), wenn von Nodes gesprochen wird
kann es alles sein (z.B. Text, Element, Attribut, Kommentar usw.).
nodeType, nodeValue, nodeName
Die Eigenschaften nodeType, nodeValue, nodeName analysieren gefundene Elementknoten, damit kann JavaScript feststellen,
ob man auch das richtige Objekt gefunden hat.
- node.nodeType / liefert Typ eines Nodes (eine Nummer)
- node.nodeValue / liefert Wert eines Nodes (Text bzw, Null)
- node.nodeName / liefert Namen eines Nodes (JavaScript-Bezeichnung eines Nodes)
nodeType
Die Eigenschaft nodeType speichert den Typ eines Knotens in Form einer Nummer.
Die Eigenschaft nodeType gibt den Typ eines Knotens in Form einer Nummer zurück und ist eine der wichtigesten Analysefunktionen bei der Navigation und Manipulation des DOM-Baums.
Das W3-Konsortium hat dazu folgende Zuordnungen festgelegt:
- Elementknoten
- Attributknoten
- Textknoten
- Knoten für CDATA-Bereich
- Knoten für Entity-Referenz
- Knoten für Entity
- Knoten für Verarbeitungsanweisung
- Knoten für Kommentar
- Dokument-Knoten
- Dokumenttyp-Knoten
- Dokumentfragment-Knoten
- Knoten für Notation
Wichtige Typen sind 1, 2 und 3. Einige dieser nodeTypes sind XML-spezifisch??
<p id="myID"></p>
<script>
var x = document.getElementById("myID");
var y = x.nodeType;
var z = x.attributes[0].nodeType;
x.innerHTML = y + " und " + z; // liefert 1 und 2
</script>
nodeValue
Javascript nodeValue gibt Inhalt eines Textknotens oder den Wert eines Attributknotens zurück.
Bei Elementknoten hat diese Eigenschaft den Wert null.
<p id="myID">Hallo Welt!</p>
<script>
var x = document.getElementById("myID");
var y = x.nodeValue;
var z = x.attributes[0].nodeValue;
var c = x.childNodes[0].nodeValue;
x.innerHTML = y + " und " + z + " und " + c; // liefert null und myID und Hallo Welt!
</script>
textContent
Die textContent-Eigenschaft findet nur den Text eines Elements und Text seiner Kindelemente.
Die textContent-Eigenschaft ignoriert alle untergeordneten Knoten und macht daraus einen einzigen Text-Knoten mit dem angegebenen String.
<p id="myID">Hallo Welt! <strong> fetter Text </strong> </p>
<script>
var x = document.getElementById("myID");
var y = x.textContent;
alert(y); // liefert Hallo Welt! fetter Text
</script>
nodeName
Javascript nodeName gibt den Namen eines Knotens zurück.
<p id="myID">Hallo Welt!</p>
<script>
var x = document.getElementById("myID");
var y = x.nodeName;
var z = x.attributes[0].nodeName;
var c = x.childNodes[0].nodeName;
x.innerHTML = y + " und " + z + " und " + c; // liefert P und id und #text
</script>
Rückgabewerte von nodeName
- Gibt den Tagnamen für element nodes zurück (in Großbuchstaben)
- Gibt den Attributnamen für attribute nodes zurück
- Gibt #text für text nodes und whitespace zurück
- Gibt #comment für comment nodes zurück
- Gibt #document für document nodes zurück
tagName
Während nodeName mit allen Knoten zurechtkommt, liefert die tagName-Eigenschaft nur den Namen eines Elementes (Tag).
Für unbekannte Knoten liefert tagName den Wert undefined.
<p id="myID">Hallo Welt!</p>
<script>
var x = document.getElementById("myID");
x.innerHTML = x.tagName; // liefert P
</script>
Parent, Child und Sibling
Die folgenden Eigenschaften greifen auf Nodes zu, die nicht direkt durch Methoden wie z.B. getElementById erreicht werden können.
- parentNode, parentElement / liefert ein Element
- childNodes[nodenumber] z.B. childNodes[0] / liefert ein Node als NodeList
- children / liefert Elemente als Collection
- firstChild, lastChild / liefert ein Node
- firstElementChild, lastElementChild / liefert ein Element
- nextSibling, previousSibling / liefert ein Node
- nextElementSibling, previousElementSibling / liefert ein Element
parentNode, parentElement
Diese beiden Eigenschaften machen dasselbe, sie liefern das übergeordnete Element eines angegebenen Elements.
Der kleine Unterschied zwischen parentElement und parentNode ist, dass parentElement den Wert null liefert, wenn der übergeordnete Knoten kein Elementknoten ist.
Das ist der Fall beim html-Element, weil dafür kein parentElement existiert.
<p id="myID">Hallo Welt!</p>
<script>
var z = document.getElementById("myID");
var x = document.documentElement.parentNode; // liefert [object HTMLDocument]
var y = document.documentElement.parentElement; // liefert null
z.innerHTML = x + " und " + y;
</script>
Den nodeName des übergeordneten Elements herausfinden:
<div>
<p id="myID">Hallo Welt!</p>
</div>
<script>
var x = document.getElementById("myID").parentNode.nodeName;
document.getElementById("myID").innerHTML = x; // liefert DIV
</script>
childNode, children
Die childNode-Eigenschaft liefert eine Sammlung (Collection) von untergeordneten Knoten (Kindknoten) als NodeList Objekt.
Aber hier gilt besondere Vorsicht, denn auch Zeilenumbrüche und Leerzeichen werden als Text-Knoten erkannt
<ul id="myID"><li>erste Liste</li><li>zweite Liste</li></ul>
<ul id="myID1"><li>erste Liste</li> <li>zweite Liste</li></ul>
<ul id="myID2">
<!--Ein Kommentar-->
<li>erste Liste</li>
<li>zweite Liste</li>
</ul>
<p id="a"></p>
<script>
function myFunction() {
var x = document.getElementById("myID").childNodes;
var y = "";
var i;
for (i = 0; i < x.length; i++) {
y = y + x[i].nodeName + "<br>";
}
document.getElementById("a").innerHTML = y;
}
myFunction();
</script>
Das NodeList-Objekt mit der ID myID liefert zwei childNodes (2x LI), weil hier kein Zeilenumbruch und Leerzeichen im Quellcode vorkommt.
Das NodeList-Objekt mit der ID myID1 liefert drei childNodes, weil die zwei LI-Tags mit einem Leerzeichen getrennt sind.
Das NodeList-Objekt mit der ID myID2 liefert sieben childNodes, dabei werden Zeilenumbrüche und der Kommentar dazugezählt.
- myID liefert LI LI
- myID1 liefert LI #text LI
- myID2 liefert #text #comment #text LI #text LI #text
Ein NodeList-Objekt ist auch mit dem Index ansprechbar.
<p id="myID">Hallo Welt! <strong> fetter Text </strong> </p>
<script>
var x = document.getElementById("myID").childNodes[0].nodeValue;
document.getElementById("myID").innerHTML = x; // liefert Hallo Welt!
</script>
children
Die children-Eigenschaft liefert nur Elementknoten, Leerzeichen, Text und Kommentare kommen nicht in die Collection wie im Beispiel zuvor.
Die children-Eigenschaft ist ebenso mit dem Index ansprechbar.
<ul id="myID">
<!--Ein Kommentar-->
<li>erste Liste</li>
<li>zweite Liste</li>
</ul>
<p id="a"></p>
<script>
function myFunction() {
var x = document.getElementById("myID").children;
var y = "";
var i;
for (i = 0; i < x.length; i++) {
y = y + x[i].nodeName + "
";
}
document.getElementById("a").innerHTML = y; // liefert LI LI
}
myFunction();
</script>
firstChild, firstElementChild, lastChild, lastElementChild
firstChild findet das erste Kind-Node der NodeList und ist ident mit elem.childNodes[0].
lastChild findet das letzte Kind-Node der NodeList und ist ident mit elem.childNodes[childNodes.length-1].
Beide Eigeschaften liefern auch Kommentare, Leerzeichen und Zeilenumbrüche.
Die firstElementChild- und lastElementChild-Eigenschaft liefern nur Elementknoten, Leerzeichen, Text und Kommentare kommen nicht in die Collection.
<p id="myID">
<!--Ein Kommentar-->
Das ist Text
<em>das ist kursiver Text</em>
<strong>das ist fetter Text</strong>
</p>
<p id="a"></p>
<script>
var x = document.getElementById("myID").firstChild;
var y = x.nodeName;
var a = document.getElementById("myID").firstElementChild;
var b = a.nodeName;
// liefert den Text des letzten Elements
var z = document.getElementById("myID").lastElementChild.innerHTML;
// liefert #text und EM und das ist fetter Text
document.getElementById("a").innerHTML = y + " und " + b + " und " + z;
</script>
nextSibling, nextElementSibling, previousSibling, previousElementSibling
Mit Siblings bietet JavaScript Zugriff auf Geschwister der aktuellen NodeList.
nextSibling findet das nächste Geschwister-Node und previousSibling das Geschwister-Node davor.
Beide Eigeschaften liefern auch Kommentare, Leerzeichen und Zeilenumbrüche.
Die nextElementSibling- und previousElementSibling-Eigenschaft liefern nur Elementknoten, Leerzeichen, Text und Kommentare werden ignoriert.
<p>
<span>Das ist Text</span>
<!--Ein Kommentar-->
<em id="myID">das ist kursiver Text</em>
<strong>das ist fetter Text</strong>
</p>
<p id="a"></p>
<script>
var x = document.getElementById("myID").previousSibling;
var y = x.nodeName;
var a = document.getElementById("myID").previousElementSibling;
var b = a.nodeName;
// liefert das ist fetter Text
var z = document.getElementById("myID").nextElementSibling.innerHTML;
// liefert #text und SPAN und das ist fetter Text
document.getElementById("a").innerHTML = y + " und " + b + " und " + z;
</script>
Anfügen und Löschen von Nodes
HTML-Elemente bzw. Nodes können mit JavaSript jederzeit angefügt und entfernt werden. Die Kunst dabei ist das richtige Node an der
richtigen Stelle. Folgende Eigenschaften werden hier vorkommen:
- document.createElement() / erzeugt ein Element-Object
- document.createTextNode() / erzeugt einen TextNode
- document.createAttribute() / erzeugt einen Attribut
- document.createComment() / erzeugt einen Kommentar
- node.appendChild() / fügt ein Node als lastChild ein
- node.insertBefore() / fügt ein Node direkt vor einem definierten Node ein
- node.removeChild() / Entfernt ein definiertes Kind-Node
- node.replaceChild() / Ersetzt ein definiertes Kind-Node
- node.hasChildNodes() / prüft ob childNode existiert (true/false)
Create Nodes und appendChild()
Bevor wir Nodes einfügen oder anfügen können muss dieses existieren. Das document-Object stellt uns dafür die create-Methoden zur Verfügung.
Im Beispiel wird ein Element (BUTTON) und ein Text (Klick mich!) erzeugt.
Mit der appendChild()-Methode wird Text in das Element als childNode angefügt.
Mit der appendChild()-Methode wird das Element (btn) als letztes childNode im body angefügt, also unterhalb von "Hallo Welt!".
<p id="myID">Hallo Welt!</p>
<script>
var btn = document.createElement("BUTTON");
var txt = document.createTextNode("Klick mich!");
btn.appendChild(txt);
document.body.appendChild(btn);
</script>
Im nächsten Beispiel wird dem Button ein onclick-Attribut zugewiesen, dass beim Klicken eine Funktion aufruft.
<p id="myID">Hallo Welt!</p>
<script>
var btn = document.createElement("BUTTON");
var txt = document.createTextNode("Klick mich!");
btn.appendChild(txt);
document.body.appendChild(btn);
var x = document.getElementsByTagName("BUTTON")[0];
var att = document.createAttribute("onclick");
att.value = "myFunction()";
x.setAttributeNode(att);
function myFunction() {
var y = document.getElementById("myID");
y.innerHTML = "Juhu das hat geklappt!";
}
</script>
Create Nodes und insertBefore()
Die insertBefore()-Methode fügt ein Node vor einem definierten Node ein.
Die Methode kommt mit zwei Parametern, der erste bestimmt das einzufügende Node, der zweite die Einfügeposition vor dem eingefügt wird.
Achtung die Einfügeposition muss existieren. Die Syntax lautet:
node.insertBefore(newnode,existingnode);
Laut Spezifikation kann das zweite Argument entfallen, die Einfügeposition ist dann das Ende von node.
Bei meinen Tests haben das allerdings nur der IE11 und Edge erkannt.
Im nächsten Beispiel wird ein p-Element mit Text erzeugt und als erstes childNode in das div-Element eingefügt.
Anstatt childNodes[0] kann auch firstChild verwendet werden,
<div id="myID"><p>Hallo Welt!</p></div>
<button onclick="myFunction()">Klick mich!</button>
<script>
function myFunction() {
var x = document.createElement("P");
var txt = document.createTextNode("Adi Prinz");
x.appendChild(txt);
var y = document.getElementById("myID");
y.insertBefore(x, y.childNodes[0]);
}
</script>
Für insertBefore() oder appendChild() muss nicht unbedingt ein neuer Node erstellt werden, es kann auch ein Node aus dem HTML sein (sowas wie copy & paste).
<ul id="myList1"><li>HTML</li><li>CSS</li></ul>
<ul id="myList2"><li>JavaScript</li><li>PHP</li></ul>
<button onclick="myFunction()">Klick mich!</button>
<script>
function myFunction() {
var x = document.getElementById("myList2").firstChild;
var y = document.getElementById("myList1");
y.insertBefore(x, y.firstChild);
}
</script>
Nodes löschen und ersetzen
Für löschen und ersetzen sind die Methoden removeChild und replaceChild da.
replaceChild braucht einen Node, welcher durch einen anderen, definioerten Node ersetz wird.
node.replaceChild(newnode,oldnode)
node.removeChild() braucht einen bestehenden Node, der definiert wird.
node.removeChild(node)
Im Beispiel wird ein li-Element erzeugt und in der dritte Listeneintrag ersetzt.
<ul id="myList">
<li>HTML</li>
<li>CSS</li>
<li>Hallo Welt</li>
<li>PHP</li>
</ul>
<button onclick="myFunction()">Klick mich!</button>
<script>
function myFunction() {
var x = document.createElement("LI");
var txt = document.createTextNode("JavaScript");
x.appendChild(txt);
var y = document.getElementById("myList");
y.replaceChild(x, y.children[2]);
}
</script>
Prüfen ob Node existiert
Im Beispiel wird zuerst geprüft, ob der definierte Node existiert, wenn ja, wird er gelöscht, sonst wird eine Meldung ausgegeben!
<ul id="myList"><li>HTML</li><li>CSS</li><li>JavaScript</li><li>PHP</li></ul>
<button onclick="myFunction()">Klick mich!</button>
<script>
function myFunction() {
var x = document.getElementById("myList");
if (x.hasChildNodes()) {
x.removeChild(x.children[0]);
} else {
alert("Es sind keine Kinder vorhanden!");
}}
</script>