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.

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

DOM

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.

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:

  1. Elementknoten
  2. Attributknoten
  3. Textknoten
  4. Knoten für CDATA-Bereich
  5. Knoten für Entity-Referenz
  6. Knoten für Entity
  7. Knoten für Verarbeitungsanweisung
  8. Knoten für Kommentar
  9. Dokument-Knoten
  10. Dokumenttyp-Knoten
  11. Dokumentfragment-Knoten
  12. 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

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:

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>

Node Methoden und Eigenschaften

Das nodeList-Object ist eines der elementaren Objekte von JavaScript, eine Auflistung der Methoden und Eigenschaften finden sie hier: