DOM: Unterschied zwischen den Versionen

Aus m-wiki
Zur Navigation springen Zur Suche springen
Zeile 110: Zeile 110:


Zuerst wird ein Eventlistener installiert, welcher beim Ereingnis "DOMContentLoaded" (siehe im oberen Abschnitt die Stati des Dokuments) aufgerufen wird. Dieser registriert dann über die [https://wiki.selfhtml.org/wiki/JavaScript/Funktion#Anonyme_Funktionen anonyme Funktion] die unterhalb stehende "click"-Funktion des ersten "#interaktiv" Elements im Dokument. Dies bedeutet, dass bei jedem Klick auf das Element die Funktion "klickverarbeitung()" aufgerufen wird. In diesem Fall bedeutet es, dass dem Element "output" jedesmal eine neue Zeile Text hinzugefügt wird.
Zuerst wird ein Eventlistener installiert, welcher beim Ereingnis "DOMContentLoaded" (siehe im oberen Abschnitt die Stati des Dokuments) aufgerufen wird. Dieser registriert dann über die [https://wiki.selfhtml.org/wiki/JavaScript/Funktion#Anonyme_Funktionen anonyme Funktion] die unterhalb stehende "click"-Funktion des ersten "#interaktiv" Elements im Dokument. Dies bedeutet, dass bei jedem Klick auf das Element die Funktion "klickverarbeitung()" aufgerufen wird. In diesem Fall bedeutet es, dass dem Element "output" jedesmal eine neue Zeile Text hinzugefügt wird.
Mit dieser Methode kann man mehrere Eventhandler pro Objekt registrieren, doch man muss das Skript ausführen, bevor das Dokument fertig geladen wurde, da es sonst nicht mehr ausgeführt wird.


=== [https://wiki.selfhtml.org/wiki/JavaScript/DOM/Element/innerHTML innerHTML] (innerText / textContent) ===
=== [https://wiki.selfhtml.org/wiki/JavaScript/DOM/Element/innerHTML innerHTML] (innerText / textContent) ===

Version vom 26. Januar 2023, 13:51 Uhr

Einführung

HTML ist wie XML eine Auszeichnungssprache und kann daher in einem DOM (Document Object Model) abgebildet werden. Das geparste Dokument ist wie ein Baum aufgebaut und beginnt mit der Wurzel (root) und bildet sich über Äste (Elementknoten) aus, welche den Tags im Dokument entsprechen und geht zu den Blättern, welche dem Inhalt das Dokuments und seiner Formatierung entsprechen.

Mittels DOM ist es nicht nur möglich, das Dokument zu analysieren und gewisse Eigenschaften zu ändern, man kann auch Elemente hinzufügen oder entfernen.

Damit die Verarbeitung korrekt erfolgen kann, muss das Dokument wohlgeformt sein. Browser wurden vom Hersteller darauf programmiert, auch feherhafte Webseiten möglichst trotzdem irgendwie anzuzeigen. Wenn man nun ein solchermassen ergänztes Dokument verarbeitet, kann es sein, dass der eigentlich korrekte Code nur bei manchen Browsern funktionert, welche das Dokument genauso ergänzen. Damit dies nicht geschieht, sollte man sein Dokument immer beim W3C validieren.

Aufbau des DOM

Beispieldokument aus dem Wiki-Artikel zum DOM.

Das Element <table> ist in diesem Beispiel "root".<thead> oder <td> sind Elementknoten "Vorname" oder "Duck" sind Textknoten (Blätter). Die Eigenschaften von Elementen, die Attribute, werden im Baum nicht dargestellt und können entsprechend auch nicht direkt erreicht werden, sondern sind Eigenschaften der Elementknoten. Bei <td textalign = "center"> ist textalign eine Eigenschaft, welche nicht direkt errreicht werden kann, sondern über document.table.tbody.tr.td(textalign) ausgelesen wird.

Jedes Element "in der Mitte" enthält entsprechend ein Elternteil (parent) und Kinder (child), wobei es immer nur einen direkten Elternteil geben kann, doch dieser mehrere Kinder haben kann. tr.parent() ist entsprechend <tbody>, doch als Kinder gibt es hier 2 <td>-Elemente, welche entsprechend über ein Array angesprochen werden müssen und in der Reihenfolge ihrer Definition auftauchen: tr.child[0]() und tr.child[1](), da auch hier ein Array immer mit 0 beginnt.

Zugriff auf Knoten

Auf jeden Knoten egal welcher Art er ist, kann mittels des Node-Objekts zugegriffen werden. Dies wird im Artikel zu Javasript aufgezeigt.

Elementknoten

Ein Elementknoten besteht aus einem HTML-Element im Elementenbaum. Man kann auf ihn mit den Eigenschaften und Methoden des Node-Interfaces, aber auch denen des Elements zugreifen.

Attributknoten

Attributknoten wurden zwar von Node abgeleitet und erben alle Eigenschaften und Methoden des Node-Interface, sind aber kein Bestandteil des DOM-Baumes. Dementsprechend finden Sie sie nicht in der childNodes Auflistung eines Node-Objekts. Attributknoten können keine Eltern- oder Kind-Knoten haben. Man sollte keine Eigenschaften oder Methoden der Node-Schnittstelle auf einen Attributknoten anwenden und stattdessen Attribute einfach als Name-Wert Paare behandeln, die einem Element zugeordnet sind.

Textknoten

Normaler Text innerhalb von Textstrukturierungs- und Textauszeichnungselementen bildet Textknoten.

Status des Dokuments

Mit Javascript kann man den Zustand abfragen. Es gibt je nach Browser verschiedene Zustände, doch folgende sind bei allen (neuen) Browsern implementiert. Eine komplette Liste wieder auf selfhtml.

  • loading -> Ladevorgang begonnen aber noch nicht abgeschlossen
  • interactive -> Die Bearbeitung ist noch nicht komplett, z. B. können noch Bilder fehlen, aber der Benutzer bekommt schon eine Seite angezeigt -> ab hier ist es sinnvoll, mit JS zu starten
  • complete -> Die Seite wird komplett im Browser gezeigt

Entsprechend des Status gibt es die Events DOMContentLoaded, welche den Status auf interactive setzen und Load, welcher complete auslöst. Daher kann man die Events mit folgendem Code auch als Alternative verwenden:

document.onreadystatechange = function () {
 if (document.readyState == "interactive") { initApplication(); }
} // end onreadystatechange 

document.onreadystatechange = function () {
 if (document.readyState == "complete") { initApplication(); }
} // end onreadystatechange

Methoden des Dokuments

Im selfhtml werden alle möglichen Elemente mit ihren Methoden aufgezeigt. Hier werden entsprechend nur die direkten Methoden des Dokuments aufgeführt. Fürs Dokument gibt es noch Eigenschaften, die auch abgefragt werden könnten:

  • close() -> wird nur gebraucht, wenn man manuell Dokumente öffnet und diese dann schliessen will.
  • createAttribute() -> damit können Elemente mit zusätzlichen Attributen (class, id usw.) versehen werden. Es braucht aber noch setAttributeNode(), um das erzeugte Attribut einem Element zuzuweisen. Wird normalerweise nicht beim Dokument selber, sondern bei einem Unterelement verwendet.
  • createDocumentFragment() -> Damit können dynamisch Elemente zum Dokument hinzugefügt gefügt werden. Es braucht aber weitere Methoden (siehe Beispiel im Link), damit die Elemente dem Dokument hinzugefügt werden.
  • createElement() -> Erzeugt ein neues Element im Dokument. Dieses muss anschliessend mit weiteren Methoden an der richtigen Stelle dem Dokumentbaum hinzugefügt werden.
  • createTextNode() -> Wie bei einem Element, doch dies erzeugt die Beschriftung oder allgemeine Texte.
  • getElementById() -> Stellt ein Element an Hand seiner einmaligen ID zur Verfügung. Gross- und Kleinschreibung wird beachtet. Kann die ID nicht gefunden werden, so wird null zurückgegeben.
  • getElementsByName() -> Gibt alle Elemente mit diesem Namen zurück. Dadurch dass mehrere Elemente zurückgegeben werden können, muss man auf die Elemente immer wie auf ein Array zurückgreifen (element[0]), auch wenn es nur ein Element mit diesem Namen gibt. Da es sich aber um eine "live node list" handelt, kann man die Elemente nicht wie ein Array und dessen Methoden anwenden.
  • getElementsByClassName() -> Auch hier erhält man eine "live node list" zurück. Die Funktion kann aber auch Elemente von mehreren Klassen zurückliefern. Dazu muss man die Funktion einfach mit allen gewünschten Klassen mittels Komma separiert aufrufen x = document.getElementsByClassName('class1', 'class2').
  • getElementsByTagName() -> Im Gegensatz zur Klasse gibt man hier als Argument das HTML-Element an, das man bearbeiten möchte (p, h2, usw.). Man kann sogar mit * alle Elemente übernehmen. Auch hier wird entsprechend eine "live node list" zurückgeliefert.
  • getSelection() -> Diese Funktion liefert den Text zurück, welchen der Benutzer markiert hat. Ohne Auswahl kommt ein leerer Text zurück.
  • importNode() -> Diese Funktion wird nur verwendet, wenn man zusätzliche Dokumente oder Teile davon ins aktuelle Dokument übernehmen möchte.
  • open() -> wird nur gebraucht, wenn man manuell weitere Dokumente öffnen möchte.
  • querySelector() -> Diese Methode sucht den ersten Treffer im Dokument(fragment) oder Element auf den der zutreffende (CSS)-Ausdruck passt. Damit kann etwa gezielt ein Abschnitt angepasst werden. document.querySelector('.beispiel').innerHTML = "Ersten Abschnitt anpassen."; Im Beispiel wird nach der CSS-Klasse beispiel gesucht und der Text in dessen Element ersetzt. Diese Methode ist Teil der Node-Klasse. Die Methode übernimmt auch mehrere Ausdrücke, welche einfach kommagetrennt sein müssen. Da man damit auch "normale" HTML-Elemente suchen/übergeben kann, ist es eine mächtige Funktion, mit welcher man auch Events an einen Knoten binden kann: document.querySelector('button').onclick = klickfunktion();. Allerdings sollte man dies nicht machen, da man so JS und HTML mischt und nur einen Handler pro Knoten verwenden kann. Man sollte dies über einen Eventhandler lösen.
  • querySelectorAll() -> Im Gegensatz zur Methode ohne "All" werden hier alle Elemente des CSS-Ausdrucks gefunden und als "live node list" zurückgeliefert. Sonst ist das Verhalten gleich.
  • write() -> Wird nur verwendet, wenn man ein zusätzliches Dokument erstellt. Um fertige Dokumente zu bearbeiten sollte stattdessen mit createTextNode gearbeitet werden, in den man dann mit appendChild Text, aber keinen HTML-Code anhängen kann.
  • writeln() -> Wie write() wird es nicht für fertige Dokumente verwendet. Im Gegensatz dazu wird noch ein Zeilenumbruch eingefügt.

Methoden eines Knotens (Node)

Ein Knoten stellt eine bestimmte "Verzweigung" im Baum dar und ist daher das zentrale Element, wenn es darum geht, ein Dokument zu bearbeiten. Neben den Eigenschaften gibt es auch hier mehrere Methoden. Da im Gegensatz zum DOkument alle für die Bearbeitung wichtig sind, werden diese nicht speziell fett gekennzeichnet.

  • Node.appendChild() -> fügt ein neues Kindelement hinzu und zwar als letztes Element. Das neue Element muss vorher mit createElement() oder createTextNode() erzeugt worden sein.
  • Node.cloneNode() -> Mit dieser Methode wird der übergebene Knoten dupliziert. Man kann auch sagen, ob er mitsamt seinen Kindelementen kopiert werden soll. Man muss beachten, dass hier auch eine ID geklont werden kann. Diese muss entsprechend angepasst werden, damit man nicht plötzlich doppelte IDs hat.
  • Node.compareDocumentPosition() -> Die Methode ermittelt, an welcher Stelle im Baum das übergebene Element, verglichen mit dem Vergleichselement ist. Der Rückgabewert ist zwischen 1 und 32 (binäre Werte) und zeigt, ob es nicht, vor, hinter usw. ist. Die Werte werden im Link aufgeschlüsselt.
  • Node.contains() -> Prüft, ob das Element im übergebenen Element enthalten ist.
  • Node.hasChildNodes() -> Prüft, ob der übergebene Knoten Kindknoten hat.
  • Node.insertBefore() -> Fügt innerhalb eines Knotens einen Kindknoten vor einem anderen Kindknoten ein. Als Parameter muss auch angegeben werden, vor welchem Element der neue Knoten eingefügt werden soll.
  • Node.isDefaultNamespace() -> überprüft, ob der im Argument genannte Namespace der Standard-Namespace des Knotens ist. Im Normalfall braucht man dies nicht zu kontrollieren. Bei komplexer Dokumentverwaltung können aber verschiedene Namespaces involviert sein.
  • Node.isEqualNode() -> Überprüft, ob zwei Knoten gleich sind. Wird z.B. verwendet um zu kontrollieren, ob ein Eintrag schon vorhanden ist, bevor man einen doppelten einfügt. Wichtig ist, dass auch Leerzeichen usw. beachtet werden, wenn man etwa manuell erstellte Abschnitte vergleicht.
  • Node.normalize() -> entfernt leere Textknoten und verbindet zusammenhängende Textknoten, die noch eigene Elemente bilden. Vor allem für den Austausch notwendig. Nur für die Ausgabe im Browser nicht relevant.
  • Node.removeChild() -> löscht aus dem übergebenen Knoten den mitgegebenen Kindknoten
  • Node.replaceChild() -> ersetzt im angegebenen Element den Kindknoten im ersten Parameter durch den Knoten im zweiten Parameter.

Mehrere Knoten bearbeiten (Nodelist)

Eine NodeList ist eine Sammlung mehrerer Knoten des DOM. Je nach Herkunft kann eine NodeList statisch oder aktive (live) sein. Eine statische NodeList ändert sich nicht mehr, auch wenn das DOM nach ihrer Ermittlung verändert wird. Im Gegensatz dazu spiegelt eine "live node list" jederzeit den aktuellen Stand des DOM-Teils wider, auf den den sie sich bezieht.

Eine NodeList kann man z.B. mit folgenden Methoden erhalten:

  • Node.childNodes - liefert eine aktive NodeList der Kindknoten eines Node-Objekts, welche bei Änderungen des DOM automatisch aktualisiert wird, da die Liste auf den aktuellen Baum verweist.
  • document.querySelectorAll - liefert eine statische NodeList der gefundenen Elemente, welche sich nicht mehr ändert, da es eine Kopie der Elemente ist.

Die Nodelist hat nur eine Eigenschaft: Nodelist.length welche angibt, wie viele Elemente die Liste enthält und welche man entsprechend für eine Iteration durch die Elemente verwenden kann.

Für den Zugriff auf die Elemente der Liste gibt es die Methode: Nodelist.item() oder man verwendet den Zugriff wie bei einem Array: nodeItem = nodeList[index];.

Zugriff auf den DOM mit Javascript

selfhtml zeigt im Artikel zum DOM, wie man mit JavaScript (JS) auf den DOM zugreift. Wie in der Einführung gezeigt, kann dies über den Gesamtbaum geschehen oder man kennzeichnet die Elemente mit einer einmaligen ID oder einem spezifischen Namen (welcher auch mehrmals vorkommen darf). Beim Zugriff über die ID hat man die Gewissheit, dass nur das gewünschte Objekt betroffen ist. Beim Zugriff über den Namen muss man schauen, dass man nicht ungewollt das falsche Objekt verwendet oder mehrere Objekte anpasst. Dies kann vor allem bei generierten Inhalten geschehen, wenn man etwa einen Teil doppelt oder aus unterschiedlichen Quellen einbindet.

Damit JS ein Element verwenden kann, muss es schon vom Browser geparst worden sein. Dies ist wichtig, da ein Skript theoretisch überall im HTML-Code eingebunden werden kann.

Eventhandler einbinden

Folgende Vorgehensweise sorgt dafür, dass die gewünschte Funktion ausgeführt wird, wenn der Inhalt im Browser fertig geladen wurde.

document.addEventListener('DOMContentLoaded', function() { 
 document.querySelector('#interaktiv').addEventListener('click', klickverarbeitung());

 function klickverarbeitung () {
   document.querySelector('output').innerText += ' Huhu, das ist von Javascript eingefügter Text. \n';
 }
});

Zuerst wird ein Eventlistener installiert, welcher beim Ereingnis "DOMContentLoaded" (siehe im oberen Abschnitt die Stati des Dokuments) aufgerufen wird. Dieser registriert dann über die anonyme Funktion die unterhalb stehende "click"-Funktion des ersten "#interaktiv" Elements im Dokument. Dies bedeutet, dass bei jedem Klick auf das Element die Funktion "klickverarbeitung()" aufgerufen wird. In diesem Fall bedeutet es, dass dem Element "output" jedesmal eine neue Zeile Text hinzugefügt wird. Mit dieser Methode kann man mehrere Eventhandler pro Objekt registrieren, doch man muss das Skript ausführen, bevor das Dokument fertig geladen wurde, da es sonst nicht mehr ausgeführt wird.

innerHTML (innerText / textContent)

function init() { document.getElementById('button').addEventListener('click',changeHTMLContent); }
 
function changeHTMLContent() {
 var neu = "neuer fetter Text";
 document.getElementById('absatz').innerHTML = neu;
}

Mit dieser Anweisung wird auf den Textteil eines Elements zugegriffen. Damit kann man dynamisch Textelemente einfügen (oder auslesen). Es ist auch möglich, damit weiteren HTML-Code einzufügen wie eine dynamische Liste. Wichtig ist aber, dass man diesen Code immer vor dem Senden an den Browser zusammensetzt, da dieser sonst etwa bei <li> automatisch ein </li> ergänzt und so nachträglich berechnete Werte ausserhalb darstellt, wie im selfhtml Wiki gezeigt. Performanter für grössere Operationen beim Auslesen eines Arrays, um Text zu generieren ist übrigens document.createTextNode. Aus Sicherheitsgründen sollte man bei reinem Text ohne Auszeichnungen innerText oder Node.textContent anwenden, auch wenn mit innerHTML kein Skript eingefügt werden können sollte.

 function auswerten() { 
   var liste = document.getElementById('liste'),

text = liste.textContent;

   document.getElementById('ausgabe').textContent = text;  
}

Wie man sieht, kann man mit textContent genauso Werte auslesen und schreiben.

Auf der selfhtml-Seite zu textContent kann man nachlesen, wie sich innerHTML, innerText und textContent zueinander verhalten und wann man warum welches Element verwenden sollte.