Kapitel 22 DOM
Die Zukunft hat viele Namen. Für die Schwachen ist sie das Unerreichbare. Für die Furchtsamen ist sie das Unbekannte. Für die Tapferen ist sie die Chance.
– Victor Hugo
JavaScript entwickelt sich natürlich weiter. Inzwischen gibt es JavaScript-Version 1.5, welche jedoch von keinem Browser unterstützt wird. JavaScript 1.5 schließt die Lücke der bisherigen JavaScript-Versionen hin zum Standard ECMAScript, 3. Revision. Dieser Sprachstandard (offizielle Spezifikation unter http://www.ecma.ch oder auf der Buch-CD) entspricht in etwa dem Sprachumfang von JavaScript 1.1, wurde aber bisher noch von keinem Browser komplett unterstützt. Der Internet Explorer 4 war sehr nahe dran, und der Internet Explorer 5 unterstützt (bis auf die üblichen Bugs) vollständig ECMAScript (oder kurz: ECMA-262). Auch Netscape 6, der im Vor-Beta-Stadium befindliche neue Browser (unter http://home.netscape.com, Entwicklerversion unter http://www.mozilla.org) ist ECMA-compliant.
Wenn jedoch im Zusammenhang mit JavaScript von Standards gesprochen wird, dann ist die Rede von DOM, dem Document Object Model. Vereinfacht gesagt, soll ein HTML-Dokument so modelliert werden, dass mit JavaScript darauf zugegriffen werden kann. Wie in den drei DHTML-Kapiteln schon angedeutet wurde, geht der Internet Explorer (ab Version 4) hier viel weiter als der Netscape Navigator 4. Die zueinander inkompatiblen DOM-Umsetzungen der beiden Browserhersteller sind für den Programmierer ein stetes Ärgernis. Da zudem noch sowohl Netscape als auch Microsoft ihre Umsetzung für die jeweils besser hielten, war zunächst keine Einigung in Sicht. Für solche Fälle gibt es glücklicherweise (mehr oder weniger) unabhängige Gremien wie das W3C, welche mittlerweile einen DOM-Standard abgesegnet haben. Die Konsequenz daraus: Der Internet Explorer 5 hält sich daran, der Netscape Navigator 6 ebenfalls (in der momentanen Version ist die Unterstützung aber vor allem
von der JavaScript-Seite aus noch überhaupt nicht vollständig). Da es bis zur endgültigen Fertigstellung des Netscape noch eine Weile dauert, und die Verbreitung des Internet Explorer 5 vor allem in Firmennetzwerken noch nicht so groß ist, ist die Zielgruppe für DOM-Effekte nicht allzu groß. Aus diesem Grund finden Sie in diesem Kapitel lediglich eine kurze Einführung in das Konzept. In einer weiteren Auflage kann das schon ganz anders aussehen.
Eine Vorbemerkung noch: Wir beschränken uns hier ausschließlich auf den DOM-Mechanismus des Internet Explorer 5.x. Der Grund hierfür ist ganz einfach der, dass der Netscape 6 – trotz offiziellem Release-Status – noch weit von der Serienreife entfernt ist und deshalb die Entwicklung damit keine Freude ist. In Kapitel 18 haben Sie gesehen, wie Sie alte DHTML-Anwendungen für den neuen Netscape fit machen.
22.1 DOM-Baum  
Das HTML-Dokument wird in einen Baum umgewandelt. Jedes HTML-Element, und auch jeder Text, der nicht von Tags umgeben ist, wird zu einem Knoten (engl. nodes) in dem Baum. Elemente zwischen einem öffnenden und dem dazugehörigen schließenden Tag sind Kindknoten (engl. child nodes), sodass auch eine Hierarchie gegeben ist. In diesem Kapitel wird folgendes Beispieldokument betrachtet:
<HTML>
<HEAD>
<TITLE>DOM-Demo</TITLE>
</HEAD>
<BODY ID="alles">
<H3 ID="Ueberschrift">DOM-Demo</H3>
<IMG SRC="ball.gif" ID="Ball1">
<IMG SRC="ball.gif" ID="Ball2">
<IMG SRC="ball.gif" ID="Ball3">
<IMG SRC="ball.gif" ID="Ball4">
</BODY>
</HTML>
In Abbildung 22.1 sehen Sie, wie der DOM-Baum für das vorherige Dokument aussieht.
Beachten Sie, dass jeder Tag ein ID-Attribut trägt. Hiermit kann der Tag dann von JavaScript aus angesprochen werden. Sie ahnen bereits, es geht wohl auch über irgendein Array mit irgendeinem Index, wie bisher immer, aber der Übersichtlichkeit halber ist es auch hier besser, sprechende Namen zu verwenden.
Abbildung 22.1 Der DOM-Baum des HTML-Dokuments
|
22.2 Navigation im Baum  
Ähnlich wie bei Framehierarchien kann man auch in dem DOM-Baum von Ebene zu Ebene springen. Die entsprechenden Methoden und Eigenschaften heißen hier nur anders, aber ansonsten bleibt alles beim Alten. Die Kindknoten eines Elements im DOM-Baum befinden sich im Array childNodes[]. Von einem Knoten (bzw. HTML-Element) aus kann man mit den folgenden Eigenschaften auf andere Knoten zugreifen:
Tabelle 22.1 Eigenschaften eines Knotens im DOM-Baum
Eigenschaft |
Beschreibung |
firstChild |
Der erste Kindknoten (erstes Element im child Nodes-Array) |
lastChild |
Der letzte Kindknoten (letztes Element im child Nodes-Array) |
nextSibling |
Das nächste Kind des Elternknotens |
parentNode |
Der Elternknoten |
previousSibling |
Das vorherige Kind des Elternknotens |
Die Eigenschaften aus Tabelle 22.2 liefern nähere Informationen über einen Knoten zurück:
Tabelle 22.2 Eigenschaften, die nähere Informationen über einen Knoten liefern
Eigenschaft |
Beschreibung |
nodeName |
HTML-Tag des Knotens als Zeichenkette (z. B. "H3") |
nodeType |
1 = Tag, 2 = Attribut, 3 = Text |
In Abbildung 22.2 sehen Sie den DOM-Baum noch einmal, und diesmal mit den Beziehungen zwischen den einzelnen Knoten.
Abbildung 22.2 Navigationsmöglichkeiten innerhalb des DOM-Baums
|
22.3 Den Baum modifizieren  
Der große Vorteil von DOM gegenüber DHTML und den früheren DOM-Implementierungen besteht darin, dass man Elemente nicht nur ändern, sondern auch entfernen und sogar neue Elemente einfügen kann. Die Baumstruktur ist hier von großem Vorteil, da das Löschen eines Knotens nicht die Baumintegrität gefährdet. In Tabelle 22.3 sehen Sie eine grobe Übersicht über die verschiedenen Methoden. Für das Beispiel in diesem Kapitel reicht sogar eine Methode.
Tabelle 22.3 Methoden zur Veränderung des DOM-Baums
Methode |
Syntax |
Beschreibung |
appendChild |
Vater.appendChild(Kind) |
Hängt Kind als Kindknoten an Vater an. |
cloneNode |
Original.cloneNode
(alles) |
Erzeugt einen Knoten, der identisch zu Original ist. Ist alles gleich true, so werden auch alle Kindknoten von
Original mit dupliziert, bei false lediglich der Knoten selbst. |
createElement |
document.createElement
(HTMLTag) |
Erzeugt einen Knoten, der aus dem in Klammern angegebenen HTML-Element (z. B. H3) besteht. |
hasChildNodes |
Knoten.hasChildNodes() |
Boolescher Wert, der angibt, ob Knoten Kinder hat oder nicht. |
insertBefore |
Vater.insertBefore(Kind, Schwester) |
Kind wird als Kindknoten von Vater eingefügt, und zwar direkt vor Schwester. |
removeNode |
Knoten.removeNode(alles) |
Knoten wird aus dem DOM-Baum entfernt. Ist alles gleich true, dann werden auch alle Kindknoten von Knoten entfernt. |
replaceNode |
Alt.replaceNode(Neu) |
Der Knoten Alt wird durch den Knoten Neu ersetzt. |
setAttribute |
Knoten.setAttribute(Name, Wert) |
Der Knoten enthält ein neues
Attribut. |
In einem kleinen Beispiel soll dies einmal ausprobiert werden. Ausgehend von obigem Beispiel-HTML-Dokument sollen die folgenden Effekte eingebaut werden:
|
Wenn der Benutzer mit der Maus auf eine der Grafiken klickt, soll diese durch eine andere Grafik ausgetauscht werden. |
|
Wenn der Benutzer noch einmal auf die Grafik klickt, soll sie komplett verschwinden (eine Art armseliges Ballerspiel mit JavaScript also). |
Der Einfachheit halber werden die Event-Handler der Grafiken schon im ursprünglichen HTML-Dokument gesetzt. Es muss also nur noch eine Funktion geschrieben werden, die überprüft, ob die Grafik schon einmal angeklickt worden ist, und dann entweder die Grafik austauscht oder den Knoten aus dem DOM-Baum entfernt.
Der folgende Code ist kurz und einleuchtend und wird daher ohne längere Vorrede abgedruckt. Leider ist noch ein kleiner Fehler drin, dazu später mehr.
<HTML>
<HEAD>
<TITLE>DOM-Demo</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function mausklick(Nummer){
var obj = document.all.alles.childNodes[1+Nummer]
var gfx = obj.src
if (gfx.indexOf("ball.gif")>-1)
obj.src="kreuz.gif"
else
obj.removeNode(false)
}
//--></SCRIPT>
</HEAD>
<BODY ID="alles">
<H3 ID="Ueberschrift">DOM-Demo</H3>
<IMG SRC="ball.gif" ID="Ball1" onClick="mausklick(1)">
<IMG SRC="ball.gif" ID="Ball2" onClick="mausklick(2)">
<IMG SRC="ball.gif" ID="Ball3" onClick="mausklick(3)">
<IMG SRC="ball.gif" ID="Ball4" onClick="mausklick(4)">
</BODY>
</HTML>
Abbildung 22.3 Die DOM-Demo im Internet Explorer 5
|
Wie Ihnen sicherlich nicht entgangen ist, spuckt der Browser hin und wieder Fehlermeldungen bei der Ausführung dieses Skripts aus, beispielsweise, wenn Sie auf die dritte Grafik von links klicken. Sehr merkwürdig! Zum Testen sollten Sie in die Funktion mausklick() den folgenden Debug-Code einfügen (am besten gleich am Anfang):
function mausklick(Nummer){
var meldung=""
for (var i=0; i<document.all.alles.childNodes.length; i++)
meldung += document.all.alles.childNodes[i].nodeName + "|" +
document.all.alles.childNodes[i].nodeType + "\n"
alert(str)
var obj = document.all.alles.childNodes[1+Nummer]
var gfx = obj.src
if (gfx.indexOf("ball.gif")>-1)
obj.src="kreuz.gif"
else
obj.removeNode(false)
}
Abbildung 22.4 Die Debug-Ausgabe
|
In Abbildung 22.4 sehen Sie die Ursache des Übels: Anscheinend befinden sich zwischen den einzelnen Grafiken noch Textstücke. Auch das ist nicht weiter verwunderlich – auch wenn ein Zeilensprung im HTML-Code in der Anzeige quasi nie Auswirkungen hat, beim DOM-Baum ist das ein Textelement. Sie sollten also die Zeilensprünge entfernen, und dann gibt es beim ersten Anklicken einer Grafik auch keine Fehlermeldungen mehr.
Um dies zu vermeiden, sollten Sie die HTML-Elemente, die Sie in Ihrem Skript verwenden wollen, mit den oben gezeigten Methoden (create Element() und appendChild() bzw. insertBefore()) erzeugen. Das obige HTML-Dokument kann auch folgendermaßen erzeugt werden (von der Klick-Funktionalität mal abgesehen) – jedoch nur ab Internet Explorer 5 und Netscape Navigator 6!
<HTML>
<HEAD>
<TITLE>DOM-Demo</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function init(){
var basis = document.all.alles
var h3 = document.createElement("H3")
h3.setAttribute("id", "Ueberschrift")
h3.innerText = "Überschrift"
basis.appendChild(h3)
for (var i=1; i<=4; i++){
img = document.createElement("IMG")
img.setAttribute("id", "Ball"+i)
img.src = "ball.gif"
basis.appendChild(img)
}
}
//--></SCRIPT>
</HEAD>
<BODY ID="alles" onLoad="init()">
</BODY>
</HTML>
Wie Sie in einer Aufgabe am Ende des Kapitels jedoch noch sehen werden, ist das vorherige Skript nicht ganz fehlerfrei. Es ist einfach gefährlich, über Index auf ein Element zuzugreifen, wenn zwischendurch Elemente entfernt werden sollen. Aber Sie haben ja bereits an anderer Stelle schon gesehen, dass es fast immer besser ist, per Identifikator auf ein Element zuzugreifen. Hier ist das auch der Fall. Es gibt eine weitere Methode des document-Objekts, und zwar getElementById(). Sie kennen sie bereits aus Kapitel 18, dort wird Sie für DHTML-Effekte des Netscape 6 eingesetzt. Als Parameter übergeben Sie den Wert des ID-Attributs des entsprechenden HTML-Tags, und die Methode gibt Ihnen eine Referenz auf das Objekt zurück. Im folgenden Code finden Sie das für unser kleines Beispiel einmal umgesetzt. Anhand der Nummer, die an mausklick() übergeben wird, kann
der Identifikator der angeklickten Grafik ermittelt werden, und dieser Wert wird dann an getElementById() übergeben.
function mausklick(Nummer){
var obj = document.getElementById("Ball"+Nummer)
var gfx = obj.src
if (gfx.indexOf("ball.gif")>-1)
obj.src="kreuz.gif"
else
obj.removeNode(false)
}
Ihnen ist sicherlich aufgefallen, dass alle Screenshots in diesem Kapitel mit dem Internet Explorer erstellt worden sind, während im restlichen Buch stets der Netscape Navigator zum Einsatz kam. Das hat einen recht einfachen Grund. Der Netscape Navigator 6, der als einziger Netscape-Browser das neue DOM-Konzept unterstützt, ist noch sehr fehleranfällig, und für den Entwickler ziemlich grausam. Auch die Beispiele in diesem Kapitel funktionieren in diesem Browser nicht, im Internet Explorer 5 und 5.5 jedoch schon. Die Unterstützung des Browsers an der Basis, den Surfern nämlich, ist auch noch relativ gering. Zwar wird anerkannt, dass der Browser für viele Plattformen zur Verfügung steht, und dass er Webstandards ziemlich genau unterstützt; die meisten Anwender wollen jedoch noch auf eine stabile Version warten. Und somit bleibt eine saubere DOM-Programmierung noch Zukunftsmusik. Die JavaScript-Programmierung wird sich in diese
Richtung bewegen – das steht außer Frage. So lange jedoch der Internet Explorer 4 und (vor allem) der Netscape Navigator 4 noch eine große Verbreitung haben, müssen Sie die Entwicklung auf diese Browser abstimmen, und sich dort mit den Inkompatibilitäten herumschlagen. Dieses Kapitel sollte Ihnen einen ersten Eindruck geben, was in Zukunft alles möglich sein wird, und wie dynamische Webseiten der Zukunft aussehen (können). Sie können sich einen Wissensvorsprung verschaffen, wenn Sie sich schon heute mit dieser Thematik beschäftigen, aber es wird noch eine lange Zeit dauern, bis Sie dieses zusätzliche Wissen auch einsetzen können. Wenn Sie diese Materie interessiert, dann lassen Sie es uns bitte über MyGalileo wissen – in einer möglichen Neuauflage werden wir bei entsprechendem Zuspruch diesen Teil erweitern.

22.4 Fragen & Aufgaben  
1. |
Das Listing ohne getElementById() hat einen Bug; klicken Sie beispielsweise stets auf die am weitesten rechts stehende Grafik. Was verursacht diesen Bug? |
2. |
Beheben Sie den Bug aus der vorherigen Aufgabe. |
3. |
Versuchen Sie, das Skript so umzuschreiben, dass auf die src-Eigenschaft der Grafik nicht zugegriffen wird. |
|