SELFHTML/Navigationshilfen Dynamisches HTML DHTML-Modelle |
Das Document Object Model (DOM) ist eine vom W3-Konsortium verabschiedete Norm, die zunächst einmal den Scriptsprachenzugriff auf beliebige Elemente eines Auszeichnungssprachen-Dokuments beschreibt. Das DOM ist also weder selber eine eigene Scriptsprache, noch ist es auf HTML beschränkt. Es definiert lediglich Objekte, Eigenschaften und Methoden, die eine Scriptsprache 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 server-seitigen Scripts, z.B. in CGI-Scripts 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 W3-Empfehlung herausgegeben, wurde es im MS 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, namlich 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 Scriptsprache 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 mit 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 Programmieren dynamischer Web-Seiten erleichtert.
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.
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); function ABC() { var Staedte = new Array(); for(var i = 0; i < document.getElementsByTagName("li").length; i++) Staedte.push(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() { var Staedte = new Array(); for(var i = 0; i < document.getElementsByTagName("li").length; i++) Staedte.push(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> |
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, definiert zunächst mit var Staedte = new Array();
einen neuen, leeren Array. 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. Dieses Information benutzt die Funktion als Abbruchbedingung für die for
-Schleife. Innerhalb der Schleife wird dem Array mit der push-Methode 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, tut 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 Alghorithmus, der die Geographie kennt. Ganz oben im Scriptbereich 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
benutzt die Funktion als aktuellen Index für die Stadt, die in die Liste geschrieben werden soll, also einfach den Zugriff auf Staedte[geoArray[i]]
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
-Arraysdie Objekt-Methode 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.
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.
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.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"><b>Top-Link?</b></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> |
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
. Allerdings ist im Beispiel firstChild.firstChild.nodeValue
notiert. Der Grund ist, dass der Verweistext nicht direkt zwischen <a>
und </a>
steht, sondern nochmal in ein b
-Element zur Fett-Auszeichnung eingeschlossen ist. Aus Sicht des a
-Elements ist also das b
-Element das firstChild
, und erst dessen firstChild
ist der sichtbare Verweistext.
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.
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.
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 = eval(document.getElementById("roterPunkt")); bp = eval(document.getElementById("blauerPunkt")); ich = eval(document.getElementById("ich")); rp.style.position = "absolute"; rp.style.top = 20; rp.style.left = 320; bp.style.position = "absolute"; bp.style.top = 320; bp.style.left = 320; ich.style.position = "absolute"; ich.style.top = 110; ich.style.left = 90; ich.style.fontFamily = "Courier New,Courier"; ich.style.fontSize = "72pt"; 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))); rp.style.left = Math.round(rpX + (rpRadius * Math.sin(rpGrad))); 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))); bp.style.left = Math.round(bpX + (bpRadius * Math.sin(bpGrad))); 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 Pbunkt"></div> <div id="blauerPunkt"><img src="ichkreis2.gif" width="20" height="20" border="0" alt="blauer Pbunkt"></div> <div id="ich">ICH</div> </body></html> |
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 Punkt 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 setzt zunächst einmal die JavaScript-Funktion eval() ein, um den Zugriff auf die div
-Bereiche 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. Durch Anwendung von eval
auf einen solchen Ausdruck wird in einer Variablen wie rp
quasi die "Bedeutung" von getElementById("roterPunkt")
gespeichert, und anschließend lässt sich mit rp
genauso arbeiten, als wenn man jedesmal wieder 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.
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.
Anzeigebeispiel: So sieht's aus
<html><head><title>Test</title> <style type="text/css"> <!-- h1 { font-family:Arial,sans-serif; font-size:24pt; font-weight:normal; border-bottom:2px solid red; } h2 { font-family:Arial,sans-serif; font-size:20pt; font-weight:normal; border-bottom:1px solid red; } h3 { font-family:Arial,sans-serif; font-size:12pt; font-weight:bold; } p { font-family:Arial,sans-serif; font-size:11pt; } --> </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") document.getElementById("User").appendChild(Elementknoten); else { var Textknoten = document.createTextNode(document.Formular.Elementinhalt.value); Elementknoten.appendChild(Textknoten); document.getElementById("User").appendChild(Elementknoten); } } //--> </script> </head><body> <form name="Formular" style="background-color:#EEEEEE; padding:10px" 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" style="background-color:#FFFFC0; padding:10px"> </div> </body></html> |
Das Beispiel enthält im sichtbaren HTML-Bereich ein Formular mit einer Auswahlliste, einem Eingabefeld und einem Klickbutton. 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. Ebenso ist es mit document.createTextNode()
. Diese Methode erzeugt einen Textknoten, aber legt in keiner Weise fest, wohin dieser Textknoten gehört. Im Beispiel wird aus dem, was der Anwender im Eingabefeld des Formulars eingegeben hat, mit document.createTextNode(document.Formular.Elementinhalt.value)
ein solcher Textknoten erzeugt. Das "Einhängen" in den Strukturbaum ist die nächste Aufgabe des Scripts.
Dazu muss das Script jedoch zwischen Elementen unterscheiden, die einen Textinhalt haben, und solchen, die keinen haben. Das hr
-Element, das der Anwender ebenfalls auswählen kann, also das HTML-Element für Trennlinien, kann keinen Textinhalt haben. Alle anderen auswählbaren Elemente dagegen können Textinhalt haben. Deshalb unterscheidet das Script mit if
und else
zwischen Typ == "hr"
und allen übrigen möglichen Werten von Typ
. Wenn der Anwender das hr
-Element ausgewählt hat, wird einfach nur das neu erzeugte und in der Variablen Elementknoten
gespeicherte hr
-Element in den Dokumentbaum eingehängt. Der Textknoten wird dagegen verworfen. In allen anderen Fällen (else
-Zweig) muss zunächst der Textknoten in den Elementknoten eingehängt werden, damit das Element den Text als Inhalt erhält. Anschließend muss das Element in den Dokumentbaum eingehängt werden.
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.
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 5.x interpretiert auch noch keine Events nach DOM-Syntax, Netscape 6.x dagegen schon. Das folgende Beispiel zeigt das Prinzip, nach dem Event-Handling nach DOM-Syntax funktioniert.
Anzeigebeispiel: So sieht's aus
<html><head><title>Test</title> <script type="text/javascript"> <!-- function Ausgeben(Text) { LogEintrag = document.createElement("li"); neuerText = document.createTextNode(Text); LogEintrag.appendChild(neuerText); document.getElementById("Log").appendChild(LogEintrag); } function handleEingabefeldClick(ev) { Event = ev; Text = "Sie haben in das Eingabefeld geklickt. "; Text = Text + "Event-Typ = " + Event.type + ", "; Text = Text + "Event-Target-Elementname = " + Event.target.nodeName + "."; Ausgeben(Text); } function handleBereichMove(ev) { Event = ev; Text = "Sie haben die Maus im Bereich bewegt. "; Text = Text + "X-Position = " + Event.clientX + ", "; Text = Text + "Y-Position = " + Event.clientY + "."; Ausgeben(Text); } function handleBereichClick(ev) { Event = ev; Text = "Sie haben in den Bereich geklickt. "; Text = Text + "X-Position = " + Event.clientX + ", "; Text = Text + "Y-Position = " + Event.clientY + "."; Ausgeben(Text); } function Init() { document.getElementsByName("Eingabe")[0].addEventListener("click", handleEingabefeldClick, true); document.getElementById("Bereich").addEventListener("mousemove", handleBereichMove, true); document.getElementById("Bereich").addEventListener("click", handleBereichClick, true); } //--> </script> </head><body onLoad="Init()"> <form name="Formular" style="background-color:#EEEEEE; padding:10px" action=""> <input type="text" name="Eingabe" size="50"> </form> <div id="Bereich" style="background-color:#FF0000; color:#FFFFFF; font-weight:bold; width:100px">Ein Bereich</div> <ol id="Log" style="font-family:Arial,sans-serif; font-size:10pt"><li>Ereignisliste</li></ol> </body></html> |
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.getElementsByName("Eingabe")[0]
, 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.
Als dritter Parameter muss addEventListener()
entweder true
oder false
übergeben werden. Mit true
schalten Sie die Event-Überwachung ein, mit false
noch nicht.
Für jedes Ereignis, das Sie überwachen wollen, benötigen Sie eine Funktion, die auf das Ereignis reagiert. 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 ev
, den sie in ihrer jeweils ersten Anweisung der Variablen Event
zuweisen. Danach können die Funktionen über die Event
-Variable auf Eigenschaften und Methoden des Ereignisobjekts zugreifen. 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.
Das ältere DHTML-Modell von Microsoft | |
Allgemeines zu Dynamischem HTML | |
SELFHTML/Navigationshilfen Dynamisches HTML DHTML-Modelle |
© 2001 selfhtml@teamone.de