JavaScript: DOM-Baum lesen und bearbeiten

Mit JavaScript können sämtliche HTML-Elemente eines Dokumentes ausgelesen und bearbeitet werden. Diese Elemente sind im Dokument hierarchisch nach dem Document Object Model (DOM) angeordnet. Die oberste Ebene dieser Hierarchie bildet immer das <html>-Element. Es enthält als Eltern- oder parent-Element seinerseits Kind- bzw. child-Elemente (<head> und <body>). Elemente werden durch ihren Elementnamen definiert und können bestimmte Attribute enthalten. Auch die Attribute der Elemente können mit JavaScript gelesen und bearbeitet werden. Elemente, die ein Start- und End-Tag besitzen, können weitere child-Elemente und reinen Text beinhalten. Alle child-Elemente des gleichen parent-Elements werden Geschwister- oder sibling-Elemente genannt.

Als Beispiel dient folgendes einfaches HTML-Dokument, das sich lose an der Idee von Unobtrusive JavaScript orientiert, bei dem HTML-Markup, CSS-Stile und JavaScript klar von einander getrennt sind. Es enthält noch keine JavaScript-Anweisungen. Diese werden im Laufe der folgenden Erläuterungen hinzugefügt, um die jeweiligen Funktionsweisen zu demonstrieren.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="de">
  <head>
    <title>JavaScript</title>
    <meta charset="UTF-8">
    <style>
      #p1 { color:red; }
      .s1 { font-size:30px; }
      .blue { color:blue; }
      .italic { font-style:italic; }
    </style>
  </head>
  <body>
    <h1>JavaScript: DOM-Baum lesen und bearbeiten</h1>
    <p id="p1" style="color:green;font-family:monospace;">Dies ist der erste <span class="s1">Absatz</span>.</p>
    <p class="blue">Dies ist der zweite Absatz.</p>
    <p class="blue italic">Dies ist der dritte Absatz.</p>
    <input id="input1" type="text" value="Eingabefeld"> 
    <input id="input2" type="checkbox" checked> 
    <input id="input3a" type="radio" name="radio1"> <input id="input3b" type="radio" name="radio1" checked> <input id="input3c" type="radio" name="radio1">
    <select>
      <option value="sel1">Hund</option>
      <option value="sel2" selected>Katze</option>
      <option value="sel3">Maus</option>
    </select><br>
    <input id="range" type="range" min="0" max="10" step="1" value="5"><br>
    <textarea cols="20" rows="3">Hallo, Welt!</textarea> <textarea cols="20" rows="3">Morgen kommt der Weihnachtsmann.</textarea>
    <script>
      /* <![CDATA[ */
      // Hier wird der JavaScript-Code der folgenden Erläuterungen eingefügt.
      /* ]]> */
    </script>
  </body>
</html>

DOM-Baum lesen

► JavaScript-Referenz: getElementsByTagName() getElementsByClassName() getElementsByName()
► JavaScript-Referenz: getElementById() querySelectorAll()

Alle Elemente des Dokuments sind im JavaScript-Objekt document enthalten und können mit verschiedenen Methoden dieses Objekts abgefragt werden, je nachdem, anhand welcher Eigenschaft ein Element identifiziert werden soll.

getElementsByTagName() gibt ein Array mit allen Elementen zurück, die den angegebenen Elementnamen haben.

getElementsByClassName() gibt alle Elemente zurück, die der angegebenen CSS-Klasse angehören.

getElementsByName() gibt alle Elemente zurück, die das angegebene name-Attribut besitzen.

getElementById() gibt das Element zurück, das das angegebene id-Attribut besitzt. Sollten im Dokument fälschlicherweise mehrere Elemente dieselbe ID besitzen, so wird das erste Element mit der gesuchten ID zurückgegeben.

querySelectorAll() findet alle Elemente, die einem bestimmten CSS-Selektor entsprechen. Also Elemente, IDs, Klassen oder Attribute.

Diese Methoden können auch verkettet werden, beispielsweise findet document.getElementById("foo").getElementsByClassName("bar") alle Elemente, die Nachkommen des Elements mit der ID foo sind und der CSS-Klasse bar angehören.

Mit der Eigenschaft length kann die Anzahl der in dem Array enthaltenen Elemente abgefragt werden.

Mit der Eigenschaft innerHTML kann bei Elementen mit Start- und End-Tag der gesamte Inhalt zwischen den Tags ermittelt werden.

Die Eigenschaft outerHTML gibt zusätzlich die umschließenden Tags mit allen Attributen zurück.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
      console.log(document.getElementsByTagName("body").length);    // 1
      console.log(document.getElementsByTagName("p").length);       // 3
      console.log(document.getElementsByClassName("blue").length);  // 2
      console.log(document.getElementsByName("foo").length);        // 0
      console.log(document.getElementById("p1").innerHTML);         // Dies ist der erste Absatz.
      console.log(document.getElementsByTagName("p")[2].outerHTML); // <p class="blue italic">Dies ist der dritte Absatz.</p>


      find = document.querySelectorAll("p");  // findet alle <p>-Elemente
      find = document.querySelectorAll("#top");  // findet das Element mit id="top"
      find = document.querySelectorAll(".blue");  // findet alle Elemente mit class="blue"
      find = document.querySelectorAll("[lang]");  // findet alle Elemente mit lang-Attribut
      find = document.querySelectorAll("[lang=de]");  // findet alle Elemente mit lang="de"

► JavaScript-Referenz: hasAttribute() getAttribute()

Mit der Methode hasAttribute() lässt sich abfragen, ob ein Element ein bestimmtes Attribut besitzt.

Mit der Methode getAttribute() lässt sich der Inhalt eines bestimmten Attributs abfragen.

Quelltext auswählen
1
2
      console.log(document.getElementsByTagName("p")[2].hasAttribute("lang")); // false
      console.log(document.getElementsByTagName("p")[2].getAttribute("class")); // blue italic

► JavaScript-Referenz: getComputedStyle() getPropertyValue()

Die Eigenschaft style ist direkt verfügbar. Über sie können alle im style-Attribut des Elements definierten CSS-Stil-Eigenschaften abgefragt werden. Da nach der JavaScript-Syntax keine Bindestriche erlaubt sind, werden zusammengesetzte Namen von CSS-Eigenschaften stattdessen in lowerCamelCase geschrieben (hier fontFamily statt font-family).

Möchte man CSS-Eigenschaften abfragen, die nicht im style-Attribut des Elements festgelegt wurden, kann man sich der Methode getPropertyValue() bedienen. Diese wird auf die Methode getComputedStyle() des window-Objekts angewendet.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
      console.log(document.getElementById("p1").style.fontFamily); // monospace
      console.log(document.getElementById("p1").getAttribute("style")); // color:green;font-family:monospace;

      el = document.getElementById("p1");
      cs = window.getComputedStyle(el);
      pv = cs.getPropertyValue("color");
      console.log(pv);  // rgb(0, 128, 0)

      console.log(document.getElementById("p1").style.color); // green
      console.log(document.getElementsByTagName("h1")[0].style.color); // [leer] (liest nur Inline-Styles)

      pv = cs.getPropertyValue("height");
      console.log(pv);  // 34 px

Bei der Verarbeitung von Formular-Elementen sind eine Reihe von Besonderheiten zu beachten.

<input type="text"> und <input type="range">:
Die Methode getAttribute("value") gibt den ursprünglich im Quelltext vorgesehenen Wert des Attributs zurück, auch wenn der Wert über die Eigenschaft value verändert wurde.
Der vom Benutzer geänderte Wert kann mit value abgefragt werden.

<input type="checkbox"> und <input type="radio">:
Die Eigenschaft value gibt den ursprünglich im Quelltext vorgesehenen Status des Elements zurück, auch wenn der Wert über die Eigenschaft checked verändert wurde.
Der vom Benutzer geänderte Wert kann mit checked abgefragt werden.

<select>:
Die Eigenschaften selectedIndex und value verhalten sich erwartungsgemäß. Auch wenn der Status des Elements mit selected geändert wurde, gibt value die geänderte Auswahl zurück.
Die vom Benutzer gewählte Option kann mit selectedIndex und value abgefragt werden.

<textarea>:
Die Eigenschaft innerHTML gibt den ursprünglich im Quelltext vorgesehenen Inhalt des Elements zurück, auch wenn dieser über die Eigenschaft value verändert wurde.
Wurde der Inhalt des Elements bereits mit value geändert, kann er nicht wieder erneut mit innerHTML geändert werden.
Der vom Benutzer geänderte Inhalt kann mit value abgefragt werden.

Beim Auslesen bzw. Einfügen von mehrzeiligem Text bei einer <textarea> ist zu beachten, dass die Escape-Sequenz für einen Zeilenumbruch je nach Plattform und Browser unterschiedlich sein kann. Obwohl klassisch \n auf unixoiden Systemen, \r unter MacOS und \r\n unter Windows verwendet wird, scheint das nicht immer der Fall zu sein. Ich habe das unter Linux mit Chromium und Firefox sowie unter WIndows 10 mit Edge und Firefox geprüft und in allen Fällen wurde \r\n und \r in \n umgewandelt. Ob man sich darauf unter allen Plattform/Browser-Konstellationen verlassen kann, entzieht sich meiner Kenntnis und sollte daher selbst geprüft werden, um fehlerhafte Ein- bzw. Ausgaben zu vermeiden!

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
      document.getElementById("input1").value = "Neue Eingabe";
      value1 = document.getElementById("input1").getAttribute("value");
      value2 = document.getElementById("input1").value;
      console.log(value1);  // Eingabefeld
      console.log(value2);  // Neue Eingabe
      
      document.getElementById("input2").checked = false;
      console.log(document.getElementById("input2").checked);  // false
      console.log(document.getElementById("input2").value);  // on
      
      document.getElementById("input3a").checked = true;
      console.log(document.getElementById("input3b").checked);  // false
      console.log(document.getElementById("input3b").value);  // on
      
      document.getElementsByTagName("select")[0].options[2].selected = true;
      console.log(document.getElementsByTagName("select")[0].selectedIndex);  // 2
      console.log(document.getElementsByTagName("select")[0].value);  // sel3
      
      document.getElementById("range").value = 9;
      value1 = document.getElementById("range").getAttribute("value");
      value2 = document.getElementById("range").value;
      console.log(value1);  // 5
      console.log(value2);  // 9
      
      document.getElementsByTagName("textarea")[0].value = "Hier steht nichts!";
      value1 = document.getElementsByTagName("textarea")[0].innerHTML;
      value2 = document.getElementsByTagName("textarea")[0].value;
      console.log("innerHTML: ", value1);  // Hallo, Welt!
      console.log("value: ", value2);  // Hier steht nichts!
      document.getElementsByTagName("textarea")[0].innerHTML = "Sein oder nicht sein.";  // Wird nicht ausgeführt.
      
      document.getElementsByTagName("textarea")[1].innerHTML = "Dann kommt er eben...";
      value1 = document.getElementsByTagName("textarea")[1].innerHTML;
      value2 = document.getElementsByTagName("textarea")[1].value;
      console.log("innerHTML: ", value1);  // Dann kommt er eben...
      console.log("value: ", value2);  // Dann kommt er eben...
      document.getElementsByTagName("textarea")[1].value = "Sein oder nicht sein.";  // Wird ausgeführt.

DOM-Baum bearbeiten

► JavaScript-Referenz: createElement() replaceChild() appendChild() insertBefore() removeChild()

Um ein neues HTML-Element im Dokument unterzubringen, wird dieses Element zunächst mit der Methode createElement() erzeugt und gegebenenfalls mit Attributen und Inhalten ausgestattet. Dann stehen verschiedene Methoden zur Verfügung, um es in den DOM-Baum einzufügen:

Mit appendChild() wird es nach dem letzten child-Element des parent-Elements eingefügt.

Mit replaceChild() kann ein existierendes Element durch das neue ersetzt werden.

Mit insertBefore() kann das neue Element vor ein existierendes eingefügt werden. Ist das existierende Element nicht über seine ID oder ein anderes Merkmal identifizierbar, kann es auch über die Eigenschaft children des parent-Elements gefunden werden, indem alle direkten child-Elemente enthalten sind.

MIt der Methode removeChild() kann ein existierendes Element schließlich auch wieder entfernt werden.

Quelltext auswählen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
      newChild = document.createElement("p");
      newChild.setAttribute("id", "neu");
      newChild.setAttribute("class", "s1");
      newChild.innerHTML = "Dies ist der neue Absatz.";
      document.getElementsByTagName("body")[0].appendChild(newChild);
      
      oldChild = document.getElementById("p1");
      newChild = document.createElement("p");
      newChild.setAttribute("id", "brandneu");
      newChild.innerHTML = "Dies ist der brandneue Absatz.";
      oldChild.parentNode.replaceChild(newChild, oldChild);

      oldChild = document.getElementById("brandneu");
      newChild = document.createElement("p");
      newChild.setAttribute("id", "ultimativneu");
      newChild.innerHTML = "Dies ist der ultimativneue Absatz.";
      oldChild.parentNode.insertBefore(newChild, oldChild);

      oldChild = document.getElementsByTagName("body")[0].children[2];  // das dritte child-Element von <body>
      newChild = document.createElement("p");
      newChild.setAttribute("id", "oberhalb");
      newChild.innerHTML = "Dieser Absatz wurde oberhalb eingefügt.";
      oldChild.parentNode.insertBefore(newChild, oldChild);

      document.getElementsByTagName("body")[0].removeChild(document.getElementsByTagName("textarea")[1]);

► JavaScript-Referenz: setAttribute()

Eine andere einfache Methode, ein existierendes Element zu ersetzen, besteht darin, seine outerHTML-Eigenschaft mit dem neuen Element zu überschreiben, das einfach komplett als String notiert wird. Ist dieser String leer, wird das Element gelöscht.

Einzelne Attribute eines Elements können mit der Methode setAttribute() eingefügt oder geändert werden.

Quelltext auswählen
1
2
      document.getElementsByTagName("textarea")[0].outerHTML = "<p class='s1'>Mit der <samp>outerHTML</samp>-Methode eingefügt.</p>";
      document.getElementById("id1").setAttribute("class", "blue");