# Das Document Object Model (DOM)

| Allgemeines zum DOM Elementinhalte dynamisch ändern HTML-Attribute dynamisch ändern CSS-Eigenschaften dynamisch ändern Neue Knoten erzeugen und in Baumstruktur einhängen Event-Handling beim DOM

## Allgemeines zum DOM

Das Document Object Model (DOM) ist eine vom W3-Konsortium verabschiedete Norm, die zunächst einmal den Programmiersprachen-Zugriff auf beliebige Elemente eines Auszeichnungssprachen-Dokuments beschreibt. Das DOM ist also weder selber eine eigene Programmiersprache, noch ist es auf HTML beschränkt. Es definiert lediglich Objekte, Eigenschaften und Methoden, die eine Programmiersprache umsetzen sollte, wenn sie sich DOM-fähig bezeichnen will. Anwendbar sollen diese Objekte, Eigenschaften und Methoden auf alle Dokumente sein, die in einer XML-gerechten Auszeichnungssprache geschrieben sind. Das W3-Konsortium betont ausdrücklich, dass das DOM nicht einfach nur eine Norm für "Dynamic HTML" sein soll. Das DOM ist auch nicht auf die Client-Seite, also etwa den Web-Browser beschränkt. Ebensogut lässt es sich in serverseitigen Programmen, z.B. in CGI-Scripten einsetzen, um Dokumente dynamisch zu erzeugen.

Wie auch immer - das DOM ist die willkommene Lösung auch für das, was hier unter dynamischem HTML verstanden wird, also das nachträgliche dynamische Verändern von Inhalten einer im Browser-Fenster angezeigten Web-Seite. 1998 erstmals als offizielle W3C-Empfehlung herausgegeben, wurde es im Internet Explorer 5.0 und in dem von der Mozilla-Entwicklergemeinde völlig neu programmierten Netscape-Browser, den Netscape selber unter der Version 6.0 anbot, implementiert (JavaScript-Version 1.5). Die Implementierung ist in den heutigen Browsern noch nicht vollständig. Immer wieder wird man als Programmierer auf rätselhafte Verhaltensweisen im einen oder anderen Browser stoßen. Dennoch ist die Richtung damit vorgegeben, wie Dynamisches HTML in Zukunft programmiert werden wird.

Eine Auszeichnungssprache wie HTML oder auch jede andere, XML-basierte Sprache ist als hierarchische Baumstruktur abbildbar. Die einzelnen Bestandteile einer solchen Baumstruktur werden als Knoten bezeichnet. Das zentrale Objekt des DOM ist deshalb das Objekt node (node = Knoten). Es gibt verschiedene Knotentypen. Innerhalb eines gewöhnlichen HTML-Dokuments gibt es auf jeden Fall drei wichtige Knotentypen, die Sie unterscheiden müssen: Elementknoten, Attributknoten und Textknoten.

Betrachten Sie zum Verständnis das folgende einfache HTML-Konstrukt:
`<h1 align="center">Hallo Welt</h1>`
In diesem Konstrukt gibt es einen Elementknoten, nämlich den Elementknoten des `h1`-Elements. Ferner gibt es einen Attributknoten, nämlich den des `align`-Attributs. Und schließlich gibt es so genannte "Zeichendaten", die an zwei Stellen vorkommen: nämlich einmal als Inhalt des `h1`-Elements, und einmal bei der Wertzuweisung an das `align`-Attribut. Diese Zeichendaten stellen selbst Knoten dar, nämlich Textknoten.

Ein weiteres Beispiel-Konstrukt soll das Verständnis vertiefen:
`<h1 align="center">Hallo <i>Welt</i></h1>`
In diesem Beispiel ist die Kursiv-Auszeichnung um das Wort `Welt` hinzugekommen. Wichtig ist dabei nun zu verstehen, wie die Knotenhierarchie aussieht:
Das `h1`-Element ist in diesem kleinen Strukturbaum-Ausschnitt der Ausgangsknoten. Dieser Knoten hat nach den Regeln des DOM zwei Kindknoten und einen assoziierten Knoten: die Kindknoten sind zum einen der Textknoten mit dem Wort `Hallo` und dem Leerzeichen dahinter, sowie der Elementknoten des `i`-Elements. Das `align`-Attribut im einleitenden `<h1>`-Tag ist dagegen kein Kindknoten, sondern ein assoziierter Knoten. Der Attributknoten hat jedoch selbst wiederum einen Kindknoten, nämlich den zugewiesenen Wert (`center`). Auch der Elementknoten des `i`-Elements hat wieder einen Kindknoten, nämlich den Textknoten seines Zeicheninhalts, also das Wort `Welt`.

Die Baumstruktur einer komplexen Web-Seite kann, wie sich aus diesen einfachen Beispielen erschließt, sehr umfangreich und tief verschachtelt sein. In einer Script-Sprache muss es aber möglich sein, möglichst schnell und effizient auf einzelne Knoten zugreifen zu können. Im DOM gibt es daher drei wichtige Methoden des document-Objekts, um zumindest auf jeden beliebigen Elementknoten direkt zugreifen zu können:
getElementById() kann auf Elemente zugreifen, die ein dokumentweit eindeutiges `id`-Attribut enthalten, z.B.
`<p id="derAbsatz">hier steht der Text</p>`
getElementsByName() kann auf Elemente zugreifen, die einen Namen besitzen (er muss nicht unbedingt eindeutig sein), z.B.
`<input name="Eingabefeld" type="text" size="40">`
getElementsByTagName() kann auf alle Elemente zugreifen in der Form: "liefere mir das 27. `td`-Element im Dokument".

Ausgehend von einem ermittelten Elementknoten lässt sich dann schnell auf dessen Attribute und Inhalt zugreifen. Auch dafür stellt das DOM Eigenschaften und Methoden bereit. Dabei gibt es jedoch bei HTML-basierten Web-Seiten zwei Varianten. Einmal können Sie die Eigenschaften und Methoden des node-Objekts dazu verwenden, um auf Kindknoten und assoziierte Knoten eines Elements zuzugreifen. Zum anderen können Sie aber auch die HTML-Elementobjekte benutzen. Vor allem für den Zugriff auf einzelne Attribute eines Elements sind die HTML-Elementobjekte gut geeignet.

Dynamisches HTML kann aber auch darin bestehen, neue Elemente mit Inhalten und Formatierungen zur Anzeigezeit einer Web-Seite neu zu erzeugen. Für diesen Zweck hält das DOM ebenfalls Methoden bereit.

In der Version 1.0 des DOM wurde nur der Zugriff auf Knoten geregelt. Der Zugriff auf zugewiesene Style-Eigenschaften und von Event-Handling ist dort noch ausgespart. In der DOM-Version 2.0 vom November 2000 sind jedoch auch diese für dynamische Web-Seiten wichtigen Gebiete behandelt. Es ist deshalb auf jeden Fall empfehlenswert, bei neu zu erstellenden dynamischen Web-Seiten von den im DOM geregelten Techniken auszugehen. Die älteren Modelle von Microsoft und Netscape sollten derzeit noch aus Gründen der Rückwärtskompatibilität eingesetzt werden, auf die Dauer jedoch langsam aus der Welt verschwinden, damit endlich ein einheitlicher Sprachenstandard das Entwickeln dynamischer Web-Seiten erleichtert.

## Elementinhalte dynamisch ändern

Es gibt viele sinnvolle Anwendungsfälle zum dynamischen Ändern von zunächst angezeigten HTML-Elementen. Das folgende Beispiel zeigt einen davon. In dem Beispiel wird eine nummerierte Liste von Großstädten zunächst alphabetisch sortiert ausgegeben. Unterhalb der Liste kann der Anwender jedoch mit Hilfe zweier Formular-Buttons zwischen alphabetischer Sortierung oder geographischer Sortierung wechseln.

### Beispiel:

Anzeigebeispiel: So sieht's aus

    <html><head><title>Test</title>
    <script type="text/javascript">
    var geoArray = new Array(6, 7, 0, 1, 4, 3, 8, 9, 2, 5, 11, 12, 10);
    var Art = "ABC";

    function ABC () {
      if (Art == "ABC")
        return;
      Art = "ABC";
      var Staedte = new Array();
      for (var i = 0; i < document.getElementsByTagName("li").length; i++)
        Staedte[Staedte.length] = document.getElementsByTagName("li")[i].firstChild.nodeValue;
      Staedte.sort();
      for (i = 0; i < document.getElementsByTagName("li").length; i++)
        document.getElementsByTagName("li")[i].firstChild.nodeValue = Staedte[i];
      document.getElementById("Art").firstChild.nodeValue = "alphabetisch von A bis Z";
    }

    function GEO () {
      if (Art == "GEO")
        return;
      Art = "GEO";
      var Staedte = new Array();
      for (var i = 0; i < document.getElementsByTagName("li").length; i++)
        Staedte[Staedte.length] = document.getElementsByTagName("li")[i].firstChild.nodeValue;
      for (i = 0; i < document.getElementsByTagName("li").length; i++)
        document.getElementsByTagName("li")[i].firstChild.nodeValue = Staedte[geoArray[i]];
      document.getElementById("Art").firstChild.nodeValue = "geographisch von Nord nach Süd";
    }
    </script>
    </head><body>
    <h1>Große Städte <span id="Art">alphabetisch von A bis Z</span></h1>
    <ol>
    <li>Berlin</li>
    <li>Dortmund</li>
    <li>Dresden</li>
    <li>Düsseldorf</li>
    <li>Essen</li>
    <li>Frankfurt</li>
    <li>Hamburg</li>
    <li>Hannover</li>
    <li>Köln</li>
    <li>Leipzig</li>
    <li>München</li>
    <li>Nürnberg</li>
    <li>Stuttgart</li>
    </ol>
    <form name="Formular" action="">
    <input type="button" name="abc" value="alphabetisch" onclick="ABC()">
    <input type="button" name="geo" value="geographisch" onclick="GEO()">
    </form>
    </body></html>

### Erläuterung:

Im Beispiel ist die nummerierte Liste zunächst mit den Städtenamen in alphabetischer Sortierfolge notiert. In dem Formular unterhalb der Liste sind zwei Klick-Buttons notiert. Der eine ruft die Funktion `ABC()` auf, der andere die Funktion `GEO()`. Ein Klick auf `ABC()` bringt natürlich nichts, da die Liste ohnehin alphabetisch sortiert ist. Also empfiehlt sich zunächst der Klick auf den Button mit der Aufschrift `geographisch`. Die Funktion `GEO()`, die er aufruft, prüft zunächst über die Variable `Art`, ob die Liste bereits geographisch sortiert ist. Wenn dies der Fall ist, wird die Funktion vorzeitig mit return ohne Rückgabewert beendet. Andernfalls erfolgt die Sortierung. Dazu wird mit zuerst `var Staedte = new Array();` ein neuer, leerer Array definiert. Das Vorhaben ist, diesen Array mit den Städtenamen aus der Liste zu füllen. Dazu greift die Funktion der Reihe nach in einer `for`-Schleife alle Elemente vom Typ `li` ab, die in dem Dokument vorkommen.

Der Zugriff erfolgt mit `document.getElementsByTagName("li")`.

Über `document.getElementsByTagName("li").length` kann die Anzahl der `li`-Elemente im Dokument ermittelt werden. Diese Information benutzt die Funktion als Abbruchbedingung für die `for`-Schleife. Innerhalb der Schleife wird dem Array der Inhalt des jeweils aktuellen `li`-Elements hinzugefügt.

Mit `document.getElementsByTagName("li")[i].firstChild.nodeValue` wird dabei auf den Inhalt des jeweiligen `li`-Elements zugegriffen. Aus DOM-Sicht ist `document.getElementsByTagName("li")[i]` ein Knoten, nämlich der `i`.te `li`-Elementknoten im Dokument. `firstChild` ist eine Eigenschaft des node-Objekts. Über diese Eigenschaft können Sie auf den ersten Kindknoten eines Knotens zugreifen. Der erste Kindknoten aller `li`-Elemente im Dokument ist deren Textinhalt, also jeweils ein Städtenamen. Die Eigenschaft `firstChild` liefert aber noch nicht den Inhalt des Elements, sondern nur das Objekt des Inhalts. Um tatsächlich an den Inhalt heranzukommen, also an den konkreten Städtenamen, muss eine weitere Eigenschaft des `node`-Objekts bemüht werden, nämlich die Eigenschaft `nodeValue`. Die Kombination `firstChild.nodeValue` können Sie sich ruhig merken. Diese Kombination werden Sie häufig verwenden, um an den Inhalt eines Elements zu kommen.

Nachdem der Array mit den Städtenamen gefüllt ist, macht die Funktion `GEO()` einfach das Umgekehrte und schreibt den Array wieder zurück in die Liste - ebenfalls in einer `for`-Schleife.
Der Ausdruck `document.getElementsByTagName("li")[i].firstChild.nodeValue` steht diesmal links von der Zuweisung. Dadurch wird dem Inhalt des Listenelements dynamisch ein neuer Wert zugewiesen. Im Beispiel ist das der etwas vertrackst aussehende Wert `Staedte[geoArray[i]]`. Die Städte sollen ja geographisch ausgegeben werden. Nun gibt es keinen Algorithmus, der die Geographie kennt. Ganz oben im Script-Bereich ist daher ein Array namens `geoArray` notiert. Die Zahlen, mit denen er initialisiert wird, sind einfach die Indexnummern der alphabetisch sortierten Städte. So hat `geoArray[0]` beispielsweise den Wert `6`. Dank dieser Information weiß die Funktion `GEO()`, dass die nördlichste der Städte diejenige ist, die in der alphabetischen Sortierung die Indexnummer 6 hat (Hamburg). Mit ``Staedte[geoArray[i]]`` benutzt die Funktion als aktuellen Index für die Stadt, die in die Liste geschrieben werden soll, also einfach den Zugriff auf `geoArray`.

Die Funktion `ABC()` geht ganz genauso vor wie die Funktion `GEO()`. Sie unterscheidet sich nur dadurch von letzterer, dass sie nach dem Einlesen des `Staedte`-Arrays die Objektmethode sort() auf den Array anwendet, um die Einträge zu sortieren. Dann schreibt sie den sortierten Array einfach zurück in die nummerierte Liste.

Ein weiteres Element wird von beiden Funktionen ebenfalls noch geändert: nämlich das `span`-Element innerhalb der Überschrift. Da für dieses Element im HTML-Code mit `id="Art"` ein ID-Wert notiert ist, lässt sich mit `document.getElementById("Art")` bequem darauf zugreifen. Mit der üblichen Syntax `firstChild.nodeValue` wird der Text des Elements angesprochen und dynamisch geändert.

## HTML-Attribute dynamisch ändern

Durch das dynamische Hinzufügen, Ändern oder Löschen von HTML-Attributen lassen sich interessante Effekte, aber auch nützliche Zwecke erreichen. Das folgende Beispiel zeigt, wie Sie Verweise dynamisch ändern können.

### Beispiel:

Anzeigebeispiel: So sieht's aus

    <html><head><title>Test</title>
    <script type="text/javascript">
    var TopLinks = new Array("http://www.spiegel.de/",
                             "http://www.heise.de/newsticker/",
                             "http://www.encarta.msn.de/",
                             "http://paperball.fireball.de/",
                             "http://kochbuch.unix-ag.uni-kl.de/");
    var aktuellerLink = 0;

    function TopLink () {
      document.getElementsByName("Ziel")[0].href = TopLinks[aktuellerLink];
      document.getElementsByName("Ziel")[0].firstChild.nodeValue = TopLinks[aktuellerLink];
      aktuellerLink += 1;
      if (aktuellerLink >= TopLinks.length)
        aktuellerLink = 0;
    }

    function TopLinksFenster (Fenster) {
      document.getElementsByName("Ziel")[0].target = Fenster;
    }
    </script>
    </head><body>
    <p><a name="Ziel" href="#Ziel">Top-Link?</a><br>
    <a href="javascript:TopLink()">Nächster Top-Link!</a><br>
    TopLinks: <a href="javascript:TopLinksFenster('_blank')">in neues Fenster laden!</a> oder
    <a href="javascript:TopLinksFenster('_self')">ins aktuelle Fenster laden!</a></p>
    </body></html>

### Erläuterung:

Das Beispiel enthält im sichtbaren Bereich einen "Top-Link", der zunächst auf sich selbst verweist (`name="Ziel" href="#Ziel"`). Unterhalb davon sind drei weitere Verweise notiert. Der erste verspricht den jeweils nächsten Top-Link zu präsentieren, und die beiden darunter erlauben es dem Anwender festzulegen, wie er die Links öffnen will - ob im gleichen oder in einem neuen Fenster. Alle diese drei Verweise rufen JavaScript-Funktionen auf, die im Dateikopf notiert sind. Der Verweis, der den nächsten Top-Link verspricht, ruft die Funktion `TopLink()` auf.

Diese Funktion greift mit `document.getElementsByName("Ziel")[0]` auf das erste Element im Dokument zu, bei dem als Attribut `name="Ziel"` notiert ist. Das ist im Beispiel bei dem Verweis der Fall, der die TopLinks anzeigen soll und aber bislang auf sich selbst verweist. Geändert werden sollen bei diesem Verweis sowohl sein `href`-Attribut als auch sein sichtbarer Verweistext. Der Zugriff auf das `href`-Attribut ist ganz einfach, weil nach der DOM-Syntax der HTML-Elementobjekte jedes denkbare Attribut eines HTML-Elements einfach eine Eigenschaft des entsprechenden HTML-Objekts darstellt. Um auf HTML-Attribute zuzugreifen, brauchen Sie also nur auf das gewünschte Element zuzugreifen und dahinter, durch Punkt getrennt, den Attributnamen als Eigenschaft notieren. Beachten Sie dabei aber die besondere Groß-/Kleinschreibung bei verschiedenen Attributen wie `bgColor`, `vSpace` oder `cellPadding`.
Mit `document.getElementsByName("Ziel")[0].href` wird im Beispiel also direkt auf den Wert des `href`-Attributs des gewünschten Verweises zugegriffen. Durch Zuweisung eines Wertes wird das Attribut dynamisch geändert. Im Beispiel wird jeweils ein neuer Wert aus dem weiter oben im Script notierten Array namens `TopLinks` zugewiesen. Als Indexzähler wird die ebenfalls zuvor schon definierte Variable `aktuellerLink` verwendet. Diese wird anschließend um 1 erhöht, damit beim nächsten Aufruf der Funktion der nächste Top-Link aus dem Array gesetzt wird. Wenn der Wert von `aktuellerLink` zu hoch wird, um noch einen Eintrag in dem Array zu finden, wird die Variable wieder auf 0 gesetzt, und beim nächsten Klick wird wieder der erste Eintrag aus dem Array serviert.
Der Zugriff auf den Verweistext erfolgt wieder über `firstChild.value`.

Die Verweise, die regeln, in welchem Fenster die Top-Links geöffnet werden sollen, rufen jeweils die Funktion `TopLinksFenster()` auf und übergeben ihr als Parameter den gewünschten Zielfensternamen. Übergeben werden die reservierten Fensternamen `_self` (Öffnen im eigenen Fenster) bzw. `_blank` (Öffnen in neuem Fenster). Die Funktion `TopLinksFenster()` benutzt wieder den bequemen Weg über die HTML-Elementobjekte, um direkt auf die `target`-Eigenschaft des `a`-Elements zuzugreifen und das entsprechende Attribut zu setzen. Es ist also kein Problem, Attribute zu setzen, die im HTML-Quelltext eines Elements nicht notiert sind.

Falls Sie mal ein Attribut löschen wollen, haben Sie zwei Möglichkeiten: entweder Sie setzen seinen Wert auf `""` (leere Zeichenkette), oder - und das ist sauberer - Sie arbeiten mit der Methode removeAttribute() des `node`-Objekts.

## CSS-Eigenschaften dynamisch ändern

Seit der DOM-Version 2.0 wird auch geregelt, wie auf Stylesheet-Angaben zugegriffen wird. Dabei hat man sich weitgehend an das seinerzeit von Microsoft eingeführte style-Objekt angelehnt.
Das folgende Beispiel zeigt, wie Sie mit Hilfe der DOM-Technik typisches, klassisches dynamisches HTML mit Bewegung positionierter Elemente schreiben. Dazu dient die Möglichkeit, Elemente mit Hilfe von CSS-Eigenschaften absolut zu positionieren.

### Beispiel:

Anzeigebeispiel: So sieht's aus

    <html><head><title>Test</title>
    <script type="text/javascript">
    var rp, bp, ich;
    var rpGeschw = 10, bpGeschw = 20;
    var rpGrad = 0, bpGrad = 0;
    var rpX = 170, rpY = 170, bpX = 170, bpY = 170;
    var rpRadius = 150, bpRadius = 150;

    function Init () {
      rp = document.getElementById("roterPunkt");
      bp = document.getElementById("blauerPunkt");
      ich = document.getElementById("ich");
      rp.style.position = "absolute";
      rp.style.top = 20 + "px";
      rp.style.left = 320 + "px";
      bp.style.position = "absolute";
      bp.style.top = 320 + "px";
      bp.style.left = 320 + "px";
      ich.style.position = "absolute";
      ich.style.top = 110 + "px";
      ich.style.left = 90 + "px";
      ich.style.fontFamily = "Courier New,Courier";
      ich.style.fontSize = "96px";
      ich.style.fontWeight = "bold";
      ich.style.color = "#009900";
      rpKreis();
      bpKreis();
    }

    function rpKreis () {
      rpGrad += rpGeschw / 1000;
      if (rpGrad > 360)
        rpGrad = 0;
      rp.style.top = Math.round(rpY + (rpRadius * Math.cos(rpGrad))) + "px";
      rp.style.left = Math.round(rpX + (rpRadius * Math.sin(rpGrad))) + "px";
      window.setTimeout("rpKreis()", 100 / rpGeschw);
    }

    function bpKreis () {
      bpGrad += bpGeschw / 1000;
      if (bpGrad > 360)
        bpGrad = 0;
      bp.style.top = Math.round(bpY + (bpRadius * Math.cos(bpGrad))) + "px";
      bp.style.left = Math.round(bpX + (bpRadius * Math.sin(bpGrad))) + "px";
      window.setTimeout("bpKreis()", 100 / bpGeschw);
    }
    </script>
    </head><body onload="Init()">

    <div id="roterPunkt"><img src="ichkreis1.gif" width="20" height="20" border="0" alt="roter Punkt"></div>
    <div id="blauerPunkt"><img src="ichkreis2.gif" width="20" height="20" border="0" alt="blauer Punkt"></div>
    <div id="ich">ICH</div>

    </body></html>

### Erläuterung:

Innerhalb des `body`-Bereichs werden im Beispiel einfach nur drei `div`-Bereiche ohne weitere Formatierungen notiert. Die ersten beiden enthalten jeweils eine kleine Grafik - `ichkreis1.gif` ist ein roter Punkt, `ichkreis2.gif` ein blauer Punkt. Beide Grafiken haben einen transparenten Hintergrund, was wichtig ist, da sich die Punkte im späteren Verlauf des Geschehens öfter überlagern werden.

Im einleitenden `<body>`-Tag ist der Event-Handler `onload` notiert. Dieser tritt in Aktion, sobald die Datei vollständig im Browser geladen ist. Dann wird die Funktion `Init()` aufgerufen, die im Dateikopf notiert ist. Diese Funktion speichert zunächst einmal Referenzen auf die drei `div`-Elemente in den drei Variablen `rp`, `bp` und `ich`, um die darauffolgenden Zugriffe auf diese Elemente zu verkürzen. Da alle drei `div`-Bereiche ein `id`-Attribut haben, ist der Zugriff auf den entsprechenden Elementknoten mit `getElementById("roterPunkt")` usw. möglich. Anschließend lässt sich mit `rp` usw. genauso arbeiten, als wenn man jedesmal wieder `document.getElementById("roterPunkt")` notieren würde.

Über die Variablen `rp`, `bp` und `ich` ist dann auch das `style`-Objekt ansprechbar. Die Funktion `Init()` stattet die drei `div`-Bereiche erst einmal mit anfänglichen CSS-Eigenschaften aus. Alle drei Bereiche werden mit `style.position = "absolute"` absolut positioniert. Die linke obere Ecke jedes Bereichs wird mit `style.left` und `style.top` bestimmt. Der Inhalt des `ich`-Bereichs, also das Wort `ICH`, wird groß und auffällig formatiert.

Am Ende ruft die Funktion `Init()` die beiden anderen Funktionen `rpKreis()` und `bpKreis()` auf. Jede dieser beiden Funktionen kontrolliert die Kreisbewegung jeweils eines der beiden Punkte, indem sie letztlich die linke obere Ecke des `div`-Bereichs, der die jeweilige Grafik enthält, neu berechnet. Dabei kommen die Kreisfunktionen für Sinus ( Math.sin()) und Cosinus ( Math.cos()) zum Einsatz. Am Ende ruft jede der beiden Funktionen sich selber rekursiv mit window.setTimeout() wieder auf, um die nächste Position des `div`-Bereichs mit der Grafik zu setzen. Die Variablen wie `bpGrad`, `bpGeschw` usw., mit denen diese Funktionen arbeiten, wurden zuvor im globalen Bereich des Scripts initialisiert.

## Neue Knoten erzeugen und in Baumstruktur einhängen

Dynamisches HTML nach DOM bedeutet nicht nur, auf bereits in HTML vorhandene Elemente zuzugreifen, sondern auch neue zu erzeugen und in den Strukturbaum einzuhängen. Wie das funktioniert, zeigt das folgende Beispiel. Es stellt einen primitiven HTML-Editor dar.

### Beispiel:

Anzeigebeispiel: So sieht's aus

    <html><head><title>Test</title>
    <style type="text/css">
    body { color:#000000; background-color:#FFFFFF; }
    #User { background-color:#FFFFC0; padding:10px; }
    form { background-color:#EEEEEE; padding:10px; }
    h1 { font-family:Arial,sans-serif; font-size:2.2em; font-weight:normal; border-bottom:2px solid red; }
    h2 { font-family:Arial,sans-serif; font-size:1.8em; font-weight:normal; border-bottom:1px solid red; }
    h3 { font-family:Arial,sans-serif; font-size:1.1em; font-weight:bold; }
    p { font-family:Arial,sans-serif; font-size:1em; }
    </style>
    <script type="text/javascript">
    function Hinzufuegen () {
      var Typ = document.Formular.Elementtyp.options[document.Formular.Elementtyp.selectedIndex].value;
      var Elementknoten = document.createElement(Typ);
      if (Typ != "hr") {
        var Textknoten = document.createTextNode(document.Formular.Elementinhalt.value);
        Elementknoten.appendChild(Textknoten);
      }
      document.getElementById("User").appendChild(Elementknoten);
    }
    </script>
    </head><body>

    <form name="Formular" action="">
    <select name="Elementtyp" size="1">
    <option value="h1">Überschrift 1</option>
    <option value="h2">Überschrift 2</option>
    <option value="h3">Überschrift 3</option>
    <option value="p">Textabsatz</option>
    <option value="hr">Trennlinie</option>
    </select>
    <input type="text" name="Elementinhalt" size="50">
    <input type="button" value="Hinzufügen" onclick="Hinzufuegen()">
    </form>

    <div id="User">
    </div>

    </body></html>

### Erläuterung:

Das Beispiel enthält im sichtbaren HTML-Bereich ein Formular mit einer Auswahlliste, einem Eingabefeld und einem Klick-Button. Unterhalb ist noch ein `div`-Bereich notiert, der jedoch noch keinen konkreten Inhalt hat. In der Auswahlliste des Formulars kann der Anwender einen HTML-Elementtyp auswählen - in den `value`-Attributen der `option`-Elemente sind die tatsächlichen HTML-Elementnamen der jeweiligen Auswahl gespeichert. Im Eingabefeld des Formulars kann der Anwender den Textinhalt für das ausgewählte Element eingeben. Wenn er dann auf den Button klickt, wird die Funktion `Hinzufuegen()` aufgerufen, die im Dateikopf in einem JavaScript-Bereich notiert ist.

Dort wird erst einmal der lange, umständliche Ausdruck `document.Formular.Elementtyp.options[document.Formular.Elementtyp.selectedIndex].value` in der Variablen `Typ` gespeichert. Der lange Ausdruck bzw. die Variable speichert den vom Anwender im Formular ausgewählten HTML-Elementtyp, also z.B. `"h1"` oder `"p"`.

Mit `document.createElement(Typ)` wird dann ein leeres, neues Element vom Typ `Typ` erzeugt, also je nach Wert der Variablen `Typ` z.B. ein `h1`-Element oder ein `p`-Element. Damit wird das Element aber noch nicht angezeigt. `document.createElement()` erzeugt lediglich den Elementknoten, hängt ihn aber noch nicht in den Strukturbaum des Dokuments ein. Im Folgenden muss das Script zwischen Elementen unterscheiden, die einen Textinhalt haben, und solchen, die keinen haben. Das `hr`-Element für Trennlinien, das der Anwender ebenfalls auswählen kann, kann keinen Textinhalt haben. Alle anderen auswählbaren Elemente dagegen können Textinhalt haben. Durch eine `if`-Anweisung wird daher abgefragt, ob die Variable `Typ` den Wert `"hr"` besitzt. Nur wenn dies nicht der Fall ist, wird der Textknoten mit der Methode `document.createTextNode()` erzeugt. Als Parameter erhält diese Methode den Text, den der Anwender ins Eingabefeld des Formulars eingegeben hat. Danach wird der Textknoten an den erzeugten Elementknoten gehängt, sodass der Text letztendlich auch sichtbar ist. In allen Fällen wird schließlich das neu erzeugte und in der Variablen `Elementknoten` gespeicherte Element in den Dokumentbaum eingehängt.

Für alle "Einhäng"-Vorgänge wird die Methode `appendChild()` des node-Objekts verwendet. Anwendbar ist die Methode auf ein Knotenobjekt, das Kindknoten haben darf. Also beispielsweise Elementknoten. Als Parameter erwartet die Methode einen Knoten, der als Kindknoten eingehängt werden soll.
Wenn im Beispiel also steht: `Elementknoten.appendChild(Textknoten);`
Dann ist `Elementknoten` eine Variable, in der zuvor durch `createElement()` ein Elementobjekt erzeugt wurde, und `Textknoten` ist eine Variable, die zuvor durch Aufruf von `createTextNode()` einen Textknoten gespeichert hat.
Mit der Anweisung `document.getElementById("User").appendChild(Elementknoten)` wird auf den zunächst leeren `div`-Bereich im Dokument zugegriffen. Diesem Element wird der neu erzeugte Elementknoten hinzugefügt.

## Event-Handling beim DOM

Die Normierung des Event-Handling im DOM war zum Redaktionszeitpunkt dieses Dokuments noch nicht abgeschlossen - so fehlte beispielsweise noch die Implementierung von Tastaturereignissen. Der Internet Explorer interpretiert auch noch keine Events nach DOM-Syntax, Netscape 6 dagegen schon. Das folgende Beispiel zeigt das Prinzip, nach dem Event-Handling nach DOM-Syntax funktioniert.

### Beispiel:

Anzeigebeispiel: So sieht's aus

    <html><head><title>Test</title>
    <script type="text/javascript">
    function Ausgeben(Text) {
      var LogEintrag = document.createElement("li");
      var neuerText = document.createTextNode(Text);
      LogEintrag.appendChild(neuerText);
      document.getElementById("Log").appendChild(LogEintrag);
    }

    function handleEingabefeldClick (Event) {
      var Text = "Sie haben in das Eingabefeld geklickt. " +
        "Event-Typ = " + Event.type + ", " +
        "Event-Target-Elementname = " + Event.target.nodeName + ".";
      Ausgeben(Text);
    }

    function handleBereichMove (Event) {
      var Text = "Sie haben die Maus im Bereich bewegt. " +
        "X-Position = " + Event.clientX + ", " +
        "Y-Position = " + Event.clientY + ".";
      Ausgeben(Text);
    }

    function handleBereichClick (Event) {
      var Text = "Sie haben in den Bereich geklickt. " +
        "X-Position = " + Event.clientX + ", " +
        "Y-Position = " + Event.clientY + ".";
      Ausgeben(Text);
    }

    function Init () {
      document.getElementById("Eingabe").addEventListener("click", handleEingabefeldClick, false);
      document.getElementById("Bereich").addEventListener("mousemove", handleBereichMove, false);
      document.getElementById("Bereich").addEventListener("click", handleBereichClick, false);
    }
    </script>
    <style type="text/css">
    #Formular { background-color:#EEEEEE; padding:10px }
    #Bereich { background-color:#FF0000; color:#FFFFFF; font-weight:bold; width:100px; }
    #Log { font-family:Arial,sans-serif; font-size:80%; }
    </style>
    </head><body onload="Init()">

    <form name="Formular" id="Formular" action="">
    <input type="text" name="Eingabe" id="Eingabe" size="50">
    </form>

    <div id="Bereich">Ein Bereich</div>

    <ol id="Log">
    <li>Ereignisliste</li>
    </ol>

    </body></html>

### Erläuterung:

Im sichtbaren Bereich der HTML-Datei ist ein Formular mit einem Eingabefeld notiert, ferner ein `div`-Bereich, und schließlich eine leere, nummerierte Liste. Im einleitenden `<body>`-Tag ist der Event-Handler `onload` notiert, der jedoch noch nichts mit Event-Handling nach DOM-Syntax zu tun hat. Dort wird einfach erst mal die Funktion `Init()` aufgerufen, die im Dateikopf notiert ist.

Innerhalb der Funktion `Init()` geht es jedoch in Sachen DOM-Event-Handling zur Sache. Event-Handling besteht beim DOM zunächst einmal darin, für einen beliebigen Knoten im Dokument eine Ereignisüberwachung zu registrieren. Dazu gibt es die Methode `addEventListener()`. Mit dem Teil davor, im Beispiel etwa `document.getElementById("Eingabe")`, wird derjenige Knoten im Dokument angesprochen, für den eine Ereignisüberwachung registriert werden soll. Im Beispiel der Funktion `Init()` werden insgesamt drei Ereignisüberwachungen angestoßen: eine für das Eingabefeld des Formulars, und zwei für den `div`-Bereich.

Die Methode `addEventListener()` erwartet drei Parameter. Der erste Parameter gibt an, welcher Ereignistyp überwacht werden soll. Bei Mausereignissen sind das weitgehend die aus dem JavaScript-Objekt event bekannten Ereignistypen wie `click`, `mouseover`, `mousedown`, `mouseup`, `mousemove`. Dazu kommen im DOM Event-Typen wie `DOMFocusIn` (Knoten erhält den Fokus), `DOMFocusOut` (Knoten verliert den Fokus), `DOMActivate` (Knoten wird durch Mausklick oder Tastendruck aktiviert), sowie anwenderunabhängige Ereignisse wie `DOMSubtreeModified` (Strukturbaum geändert), `DOMNodeInserted` (Knoten in Strukturbaum eingefügt) oder `DOMNodeRemoved` (Knoten aus Strukturbaum entfernt). Der Name des gewünschten Ereignistyps muss bei der Parameterangabe in Anführungszeichen stehen.

Der zweite Parameter, den `addEventListener()` erwartet, ist der Name einer Funktion, die beim Eintreten des Events aufgerufen werden soll. Der Name der Funktion wird ohne Anführungszeichen angegeben. Die angegebene Funktion bekommt automatisch ein Ereignisobjekt übergeben und kann dann damit anfangen was sie will. Dazu weiter unten mehr.

Die Ereignisse werden beim DOM-Event-Handling in drei Phasen verarbeitet: 1. die Capture-Phase (engl. einfangen), 2. die Target-Phase (engl. Ziel) und 3. die Bubbling-Phase (engl. aufsteigen). In der Capture-Phase läuft das Ereignis von dem äußersten Element des DOM-Baums (dem `html`-Element) zu dem Ziel-Element, für das das Ereignis bestimmt ist. Auf diesem Weg werden alle Event-Handler ausgelöst, die für die passierten Elemente für die Capture-Phase registriert sind. In der Target-Phase werden alle Event-Handler ausgelöst, die für das Ziel-Element registriert sind. In der Bubbling-Phase schließlich wandert das Ereignis schließlich vom Ziel-Element über dessen Eltern-Elemente zurück zum äußersten Element. In dieser Phase werden alle Event-Handler ausgelöst, die für die passierten Elemente für die Bubbling-Phase registriert sind. Nicht bei allen Ereignistypen steigen die Ereignisse auf (Bubbling).

Mit dem dritten Parameter von `addEventListener()` können Sie bestimmen, in welcher Phase ein Event-Handler aktiv wird. Mit `true` wird die Handler-Funktion in der Capture-Phase aufgerufen, mit `false` in der Target- oder Bubbling-Phase. In der Regel sollten Sie `false` wählen. Dieses Verhalten stellt einen Kompromiss zwischen der Vererbung bei Events (Event-Bubbling) bei dem älteren DHTML-Modell von Microsoft und der Funktion captureEvents() bei dem älteren DHTML-Modell von Netscape dar.

Für jedes Ereignis, das Sie überwachen wollen, benötigen Sie eine Funktion, die auf das Ereignis reagiert - die sogenannte Handler-Funktion. Es ist die Funktion mit dem Namen, der bei `addEventListener()` als zweiter Parameter angegeben wird. Im obigen Beispiel sollen drei Ereignisse überwacht werden. Deshalb gibt es drei ähnliche Funktionen - mit den Namen `handleEingabefeldClick()`, `handleBereichMove()` und `handleBereichClick()`. Alle drei Funktionen bekommen beim automatischen Aufruf, der beim Eintritt des Ereignisses passiert, das Ereignis als Objekt übergeben. Die Funktionen im Beispiel erwarten deshalb einen Parameter `Event`. Dieses Ereignisobjekt besitzt Eigenschaften, die Informationen zum Ereignis bereitstellen, und Methoden, mit denen sich die Ereignisverarbeitung steuern lässt. Bei Mausereignissen gibt es beispielsweise Eigenschaften wie `clientX` (Anzahl Pixel vom linken Rand des sichtbaren Knotenbereichs), `clientY` (Anzahl Pixel vom oberen Rand des sichtbaren Knotenbereichs), `screenX` (Anzahl Pixel vom linken Bildschirmrand) oder `screenY` (Anzahl Pixel vom oberen Bildschirmrand).

Im Beispiel stellen die Funktionen, die ein Ereignis überwachen, einfach nur einen Text zusammen und rufen dann die Funktion `Ausgeben()` mit dem zusammengestellten Text auf. Die Funktion `Ausgeben()` erzeugt zuerst mit `document.createElement("li")` ein neues HTML-Element vom Typ `li`. Dann erzeugt sie mit `document.createTextNode(Text)` einen neuen Textknoten mit dem übergebenen Text. Schließlich hängt sie den Textknoten mit `appendChild()` als Kindknoten in den erzeugten `li`-Elementknoten ein, und diesen in die im Dokument notierte nummerierte Liste. Auf diese Weise füllt sich die nummerierte Liste dynamisch mit jedem Ereignis, das erkannt wurde.

### Beachten Sie:

Noch nicht alle Browser, die prinzipiell Ereignisüberwachung nach DOM-Syntax unterstützen, führen Handler-Funktionen, die für die Capture-Phase registriert sind, korrekt aus. Solche Handler-Funktionen werden nur aktiviert, wenn sich das Ziel-Element des Ereignisses unterhalb des Elements befindet, bei dem die Handler-Funktion für die Capture-Phase registriert wurde. Mozilla Firefox und Safari rufen die Funktion irrtümlicherweise trotzdem auf.

Im Beispiel wurde die herkömmliche Methode `onload="Init()"` verwendet, um die Funktion `Init()` beim abgeschlossenen Laden der HTML-Datei aufzurufen, um darin die restliche Ereignisüberwachung zu starten. Diese ist theoretisch durch folgende Konstruktion ersetzbar:

    document.addEventListener("load", Init, false);

Allerdings passiert im Mozilla Firefox 1.5 kein `load`-Ereignis am `document`-Objekt, obwohl dies im DOM-Standard so vorgeschrieben ist. Eine Alternative wäre die Verwendung von `window.addEventListener("load", Init, false)`. Dies wird zwar breiter unterstützt, allerdings liegt das `window`-Objekt außerhalb des DOM-Standards.

| Das ältere DHTML-Modell von Microsoft
| Allgemeines zu Dynamischem HTML

© 2007 Impressum