Bitte warten...

JavaScript: DOM-Baum lesen und verändern

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-Element (parent) seinerseits Kind-Elemente (children: <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 Kind-Elemente und reinen Text beinhalten. Alle Kind-Elemente des gleichen Eltern-Elements werden Geschwister-Elemente (siblings) 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.

Code kopieren
<!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>
    <div id="div1"></div><p id="p1" style="color:green;font-family:monospace;">Dies ist der erste <span class="s1">Absatz</span>.</p><div id="div2"></div>
    <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.

Code kopieren
      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: parentNode parentElement previousSibling nextSibling() firstChild lastChild

Über die folgenden Eigenschaften kann das Umfeld eines Elements abgefragt werden. Hierbei ist zu beachten, dass die zurückgegebenen Objekte nicht nur HTML-Elemente, sondern auch enthaltener Text sein können, auch wenn dieser lediglich aus Leerzeichen besteht.

Die Eigenschaft parentNode gibt den Elternknoten zurück.

Die Eigenschaft parentElement gibt das Eltern-Element zurück.

Die Eigenschaft previousSibling gibt das vorangehende Geschwister-Objekt zurück.

Die Eigenschaft nextSibling gibt das folgende Geschwister-Objekt zurück.

Die Eigenschaft firstChild gibt das erste Kind-Objekt zurück.

Die Eigenschaft lastChild gibt das letzte Kind-Objekt zurück.

Code kopieren
      el = document.getElementById("p1");
      console.log(el.parentNode);       // <body>…</body>
      console.log(el.parentElement);    // <body>…</body>
      console.log(el.previousSibling);  // <div id="div1"></div>
      console.log(el.nextSibling);      // <div id="div2"></div>
      console.log(el.firstChild);       // "Dies ist der erste "
      console.log(el.lastChild);        // "."

► 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.

Code kopieren
      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.

Code kopieren
      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 (s. auch Formular-Events).

<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!

Code kopieren
      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 KInd-Element des Eltern-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 Eltern-Elements gefunden werden, indem alle direkten Kind-Elemente enthalten sind.

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

Code kopieren
      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()removeAttribute()

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.

Mit removeAttribute() kann das Attribut wieder entfernt werden.

Code kopieren
      document.getElementsByTagName("textarea")[0].outerHTML = "<p class='s1'>Mit der <samp>outerHTML</samp>-Methode eingefügt.</p>";
      document.getElementById("id1").setAttribute("class", "blue");
      document.getElementById("id1").removeAttribute("class");

Elemente und Attribute aus spezifischem Namensraum

► JavaScript-Referenz: createElementNS() setAttributeNS() removeAttributeNS()

Die Methoden createElement() und setAttribute() erzeugen Elemente bzw. Attribute in durchgehender Kleinschreibung, auch wenn in den übergebenen Namen Großbuchstaben vorhanden waren. Für HTML mag das sinnvoll sein, da Kleinschreibung hier die Norm ist. In anderen Auszeichnungssprachen, wie beispielsweise SVG, werden allerdings Element- und Attributnamen in lowerCamelCase-Schreibweise verwendet, die auch Großbuchstaben enthalten.

Um solche Elemente und Attribute mit JavaScript generieren zu können, werden die Methoden createElementNS() und setAttributeNS() verwendet, bei denen explizit der betreffende Namensraum angegeben werden muss, in dem die gewünschten Bezeichner gültig sind.

Code kopieren
      newChild = document.createElementNS("http://www.w3.org/2000/svg","feGaussianBlur");
      newChild.setAttributeNS("http://www.w3.org/2000/svg", "stdDeviation", 30);