Hoch Inhalt dieses Kapitels:

Linkwerk
screeneXa

Erhältlich u.a. bei  Amazon.de

9

Drei Einführungen in DHTML

Von Wolfgang Schwarz

9.1 Grundlagen

Lange Zeit waren Webseiten eher starre Gebilde. Ein Dokument konnte, nachdem es einmal fertig geladen war, praktisch nicht mehr verändert werden. Das höchste der Gefühle waren Image-Rollover und Scrolltexte (wahlweise in Formularfeldern oder der Statuszeile), die deshalb geradezu epidemische Verbreitung fanden. Als im Sommer 1997 der Netscape Navigator 4 und etwas später Internet Explorer 4 erschienen, wurde alles anders.

Seither kann man Seitenelemente auch nach dem Laden noch herumschieben, sie verstecken und verändern, Animationen erzeugen, Menüpunkte ein- und ausfahren, Texte überschreiben, usw. - und das ganz ohne Plugins, nur mit Hilfe von JavaScript. Die neuen Möglichkeiten gehen so weit über alles Bisherige hinaus, dass man ihnen, mehr oder weniger inoffiziell, einen eigenen Namen gab: "Dynamic HTML", oder kurz: DHTML.

Bevor Sie jetzt in Euphorie verfallen: Die Geschichte hat eine Kehrseite. Das Objektmodell, über das uns HTML-Elemente als JavaScript-Objekte zugänglich sind, ist in Sachen DHTML bei Netscape und Internet Explorer leider nicht identisch - nicht einmal ähnlich. Es ist deshalb im Allgemeinen nicht möglich, denselben Effekt auf beiden Browsern mit denselben Anweisungen zu erreichen. Diese Tatsache legt schwere Steine auf den Weg des DHTML-Programmierers: Nicht nur muss man beide Objektmodelle im Kopf haben, auch werden die Skripte durch Browserabfragen und Verzweigungen oft überdimensional aufgebläht.

Um diese viel beklagte Situation zu verbessern, hat das World Wide Web Consortium (W3C), Wächter über einheitliche Standards im Internet, mit seiner DOM-Spezifikation das einheitliche Objektmodell zukünftiger Browser vorgestellt. Da man aber weder das Modell von Netscape noch das von Microsoft einfach übernehmen wollte, gibt es nun drei Modelle, die beachtet werden müssen: Das von Netscape 4, das von Internet Explorer und das des W3C, welches zum Beispiel im neuen Netscape 6 (und teilweise auch im Internet Explorer 5) umgesetzt wurde.

In diesem Kapitel werden wir die drei Objektmodelle der Reihe nach durchgehen. Spätestens jetzt sollten Sie sich eine kleine Testumgebung einrichten. Sie brauchen für die Übungen auf jeden Fall eine Version von Netscape 4, einen Internet Explorer ab 4 und einen Browser, der das W3C-DOM unterstützt. Am besten nehmen Sie dazu Netscape 6, es reicht aber auch Internet Explorer 5.

Bevor wir mit den einzelnen Objektmodellen anfangen, machen wir einen kurzen Abstecher, um uns eine HTML-Erweiterung anzusehen, ohne die DHTML nicht möglich wäre: Cascading Style Sheets.

9.1.1 Exkurs: Cascading Style Sheets

Cascading Style Sheets (CSS) sind eine Technik, mit sich genau festlegen lässt, wie die Elemente einer HTML- oder XML-Seite im Browser dargestellt werden sollen. HTML allein bietet darauf nur wenig Einflussmöglichkeit, etwa über den so allgegenwärtigen wie veralteten <font>-Tag.

Mit Style Sheets sind sehr detaillierte Angaben möglich, zum Beispiel können Sie bestimmen, dass alle Links auf Ihrer Seite in 15 Pixel hoher, grüner Arial-Schrift auf gelbem Hintergrund mit rotem Rahmen und 2 Pixel Innenabstand zwischen Schrift und Rahmen dargestellt werden sollen. In HTML müssten Sie dafür ein barockes Konstrukt aus Hilfstabellen und Grafiken um jeden Link platzieren, in CSS dagegen benötigen Sie nur eine Zeile, die im head stehen kann und damit sauber vom Seiteninhalt getrennt bleibt.

Style Sheets wurden von Internet Explorer ziemlich bruchstückhaft bereits in Version 3 unterstützt, seither ist die Umsetzung immer weiter verbessert worden. Der Netscape Navigator 4 versteht ebenfalls die wichtigsten CSS-Angaben, erst mit Version 6 hat Netscape aber in dieser Beziehung den Microsoft-Konkurrenten eingeholt.

Style Sheets definieren

CSS-Syntax

Die Syntax von CSS ist sehr einfach. Um alle Überschriften erster Ordnung auf einer Seite in blauer Schrift erscheinen zu lassen, brauchen Sie lediglich folgende Zeilen ins head-Element einzufügen:

<style type="text/css">
<!--
h1 { color:blue }
-->
</style>

Die Kommentare dienen wie bei script-Elementen dazu, den Inhalt vor alten Browsern zu verstecken. Ich werde sie, um nicht unnötig Bäume zu verschwenden, in den hier abgedruckten Beispielen jedoch weglassen.

Schauen Sie sich jetzt die CSS-Angabe an: Sie beginnt mit dem Tag-Namen des Ziel-Elements (h1), dann folgt in geschweiften Klammern die Eigenschaft, die wir für das Element festlegen wollen. Mehrere Angaben werden einfach durch Semikolons getrennt:

h1 { color:blue; font-size:16pt; background-color:#ffff33 }

Diese Definition wirkt sich auf alle h1-Elemente des Dokuments aus. Für den Einsatz in DHTML werden wir aber häufig ein HTML-Element einzeln mit CSS formatieren wollen. Auch das ist möglich. Dazu geben wir dem Element im Start-Tag eine ID:

<div id="irgendEinName">irgend ein Inhalt</div>

Über diese ID können wir uns dann im style-Bereich auf das Element beziehen:

#irgendEinName { font-weight:bold }

Der ID wird also ein # vorangestellt. IDs dürfen bekanntlich nicht mehrfach vergeben werden. Soll eine Angabe für zwei oder mehr Elemente gelten, verwenden Sie stattdessen den class-Selektor. Dieser beginnt mit einem Punkt:

.sehrSchoen { color:green; background-color:red }

Zwei Elemente dieser Klasse könnten etwa so aussehen:

<h3 class="sehrSchoen">sehr schöne Überschrift</h3>
<p class="sehrSchoen">sehr schöner Absatz</p>

Style Sheets einbinden

externe und interne Styles

Es gibt verschiedene Möglichkeiten, Style Sheets in eine HTML-Seite einzubinden. Eine davon haben Sie schon kennen gelernt: das Element style im Kopf der Seite. Diese Technik ist besonders geeignet für Angaben, die sich lediglich auf ein einzelnes Dokument beziehen.

Soll eine CSS-Definition dagegen nicht nur für eine Seite, sondern vielleicht für einen ganzen Webauftritt gelten, dann können Sie sie in einer externen Datei ablegen. Speichern Sie dazu die CSS-Angaben als Text-Datei mit der Endung css. Auf Ihren Seiten binden Sie diese dann mit dem link-Tag ein:

<link rel="stylesheet" type="text/css" href="meinStil.css">

Style Sheets, die sich nur auf ein einziges Element beziehen, können noch auf eine andere Weise definiert werden, indem man nämlich direkt in den Start-Tag des Elements ein style-Attribut einfügt:

<p style="color:green; background-color:red">sehr schön</p>

CSS-Angaben

CSS-Eigenschaften

Die aktuelle CSS-Spezifikation des W3C, im Internet unter http://www.w3.org/TR/REC-CSS2/ zu finden, enthält 122 CSS-Eigenschaften. Tabelle 9.1 fasst diejenigen zusammen, die es sich lohnt auswendig zu lernen.

CSS-EigenschaftBeispielErklärung
colorcolor: #990000die Schriftfarbe des Elements
background-colorbackground-color: reddie Hintergrundfarbe des Elements
font-familyfont-family: Arialdie zu verwendende Schriftart
font-sizefont-size: 12ptdie Schriftgröße
text-aligntext-align: centerdie horizontale Ausrichtung des Element-Inhalts
widthwidth: 10cmdie Breite des Elements
heightheight: 200pxdie Höhe des Elements
marginmargin: 2mmder Abstand des Elements zu seinen Nachbar-Elementen
paddingpadding: 10pxder Innenabstand zwischen Element-Grenzen und -Inhalt

Tabelle 9.1: ein paar mit Style Sheets definierbare Eigenschaften

Bei vielen CSS-Attributen muss eine Maßeinheit gewählt werden. Größenangaben z.B. für Abstände oder Schriften können in den Einheiten pt, pc, in, cm, mm, em, ex, px und % festgelegt werden.

Nicht ganz so groß ist die Format-Auswahl bei Farbangaben: Hier steht Ihnen wie in HTML neben einer langen Liste vordefinierter Farbnamen wie blue, silver oder tomato die RGB-Schreibweise zur Verfügung. Diese kann nicht nur hexadezimal erfolgen - wo Rot zum Beispiel #ff0000 ist -, sondern auch dezimal und in Prozentwerten. Rot wäre also auch rgb(255, 0, 0) und rgb(100%, 0%, 0%).

Beispiel

Das folgende Beispiel definiert einen 300 Pixel breiten Absatz mit dunkelroter Schrift auf beigefarbenem Hintergrund und 5 Millimeter Abstand zwischen Schrift und Element-Aussenkante. Die Schriftart ist Verdana. Wenn Verdana nicht verfügbar ist, soll Helvetica gesucht werden; gibt es auch diese nicht, wird eine Standard-Schrift ohne Serifen gewählt.

<p style="background-color:beige; color:#660000;
   font-family: Verdana, Helvetica, sans-serif;
   padding:5mm; width:300px">
Den Stil verbessern, das heisst den Gedanken verbessern.
</p>

(Netscape 4 zeigt den Hintergrund nicht korrekt an. Wie man diesen Fehler umgeht, erfahren Sie in Übung 12.)

CSS-Positionierung und Block-Effekte

Die für DHTML wichtigsten CSS-Eigenschaften habe ich bislang verschwiegen: Mit Style Sheets können Sie nämlich nicht nur bestimmen, wie ein Element aussehen (und klingen) soll, sondern auch, wo es im Browserfenster steht, wie viel von ihm zu sehen ist, und welche andern Elemente es verdecken darf.

Um ein Element pixelgenau im Browserfenster zu positionieren, sind drei Angaben nötig. Zuerst muss mit position festgelegt werden, relativ zu welchem Punkt positioniert werden soll: entweder relativ zur normalen Position des Elements im Dokumentfluss oder relativ zur linken oberen Dokumentecke. Die entsprechenden Werte sind relative und absolute. (Steht das Element in einem anderen positionierten Element, dann ist eine "absolute" Angabe immer relativ zur Position des umgebenden Elements.) Mit Hilfe der Eigenschaften left und top wird dann der Abstand von diesem Punkt festgelegt. Diese Werte können auch negativ sein.

Ein Beispiel:

Beispiel

<body>
Das ist<br>
nicht besonders
<div style="position:absolute; left:20px; top:10px;">
   gut zu lesen.
</div>
</body>

Durch die Angabe von position:absolute wird der div-Bereich aus dem normalen Dokumentfluss herausgenommen und mit einem Abstand von 10 Pixeln zur oberen und 20 Pixeln zur linken Seitenkante positioniert. Dadurch überlappt er den im Quelltext zuvor angegebenen Text und ist deshalb nicht besonders gut zu lesen.

Tipp

Es gibt Elemente, bei denen macht eine Positionierung nur wenig Sinn, <td> zum Beispiel. Sie können unangenehme Überraschungen vermeiden, wenn Sie sich vor allem auf div- und span-Elemente beschränken, da diese von sich aus keine besonderen Formateigenschaften mit sich bringen.

Standardmäßig sind alle auf einer Seite definierten Elemente sichtbar. Mit der CSS-Eigenschaft visibility können aber auch unsichtbare Elemente erzeugt werden:

<div style="visibility:hidden">
Das ist überhaupt nicht gut zu lesen.
</div>

Wozu das gut sein soll, werden Sie demnächst erfahren.

Es ist sogar möglich festzulegen, dass nur ein Stück eines Elements zu sehen ist. Dazu dient die Eigenschaft clip, der ein Rechteck mit vier Kanten-Werten zugewiesen wird:

"
Das ist nur teilweilse gut zu lesen.
</div>

Der erste Wert bestimmt den Abstand der Oberkante des sichtbaren Bereichs zur oberen Elementgrenze, der zweite die Entfernung zwischen der rechten Kante des sichtbaren Ausschnitts und der linken Elementgrenze, der dritte die zwischen der Unterkante des Ausschnitts und der oberen Elementgrenze, der vierte die zwischen linker Elementgrenze und linkem Rand des angezeigten Ausschnitts. Alle Angaben sind also relativ zur linken oberen Element-Ecke.

Schließlich können Sie mit der Eigenschaft z-index noch die Reihenfolge festlegen, in der sich positionierte Elemente überlappen, das heißt: welches Element über welchem liegt. Der Wert von z-index ist einfach eine Zahl: Je höher die Zahl, desto höher liegt das Element über den anderen Elementen im Dokument.

Tabelle 9.2 fasst die in diesem Abschnitt vorgestellten CSS-Eigenschaften zusammen und beendet damit unseren kleinen Ausflug in die Welt der Style Sheets.

CSS- EigenschaftBeispielErklärung
positionposition: absolutedie Art der Positionierung (relativ oder absolut)
leftleft: 100pxder horizontale Abstand zum normalen Ort des Elements bei relativer Positionierung, sonst der Abstand zum übergeordneten Element
toptop: -50pxder entsprechende vertikale Abstand
visibilityvisibility: hiddendie Sichtbarkeit des Elements
clipclip: rect(20px 50px 30px 0px)der sichtbare Ausschnitt des Elements
z-indexz-index: 60die "Höhe" des Elements in der Stapelreihenfolge

Tabelle 9.2: noch ein paar CSS-Eigenschaften

9.1.2 DHTML mit dem Netscape Navigator 4

Anders als die neueren Objektmodelle von Microsoft und W3C bietet Netscape 4 nur sehr eingeschränkten Zugriff auf die Elemente einer Webseite. Es gibt lediglich einen einzigen wirklich flexiblen Element-Typ: Layer. "Layer" bedeutet "Ebene", und genau so sollten Sie sich Layer auch vorstellen:

Eine Webseite kann aus verschiedenen Ebenen bestehen, von denen jede ein eigenes kleines Dokument enthält. Diese Dokument-Ebenen können nicht nur wie in Framesets nebeneinander, sondern nach Herzenslust übereinander und ineinander liegen und noch dazu jederzeit verschoben, versteckt, verdeckt und neu beschrieben werden.

Layer in HTML

<layer>

Um eine Seite bequem in mehrere Dokument-Ebenen einzuteilen, gibt es bei Netscape 4 den neuen HTML-Tag <layer>. Sehen wir uns ein Beispiel an:

<body>
Die Sprengung erfolgt mit
<layer id="dynamitLayer" left="20" top="100">
<b>Dynamit</b>
</layer>
</body>

Was zwischen <layer> und </layer> steht, also der Inhalt des Layer-Elements, bildet ein eigenständiges Dokument, welches an der im Start-Tag angegebenen Stelle pixelgenau positioniert wird. In unserem Fall besteht es nur aus dem einen, fett formatierten Wort "Dynamit". Im Prinzip können Sie aber ein vollständiges Dokument von <html> bis </html> in den Layer schreiben. Es ist sogar möglich, externe Seiten einzubinden:

Sie sehen, Layer sind anders als gewöhnliche HTML-Elemente. So anders, dass kein Browser außer Netscape 4 sie versteht - nicht einmal die Nachfolgeversion Netscape 6. Deshalb sollten Sie in der Praxis besser auf <layer> verzichten.

<layer>-Ersatz

Das geht, weil Netscape einen Ersatz zur Verfügung stellt. Gewöhnliche HTML-Elemente werden nämlich intern in Layer verwandelt, wenn man ihnen mit CSS-Angaben eine absolute oder relative Positionierung verleiht. Das folgende Dokument entspricht deshalb intern genau dem Beispiel vom Anfang dieses Abschnitts:

<body>
Die Sprengung erfolgt mit
"</body>

Diese Schreibweise wird im Gegensatz zu <layer> auch von Internet Explorer und Netscape 6 verstanden. Wenn ich im Weiteren von Layern spreche, dann meine ich Objekte vom Typ Layer, egal ob sie mit dem <layer>-Tag oder mit <div>, <span> oder <p> erzeugt wurden.

Layer in JavaScript

Das Objekt Layer

In JavaScript gibt es bei Netscape 4 den speziellen Objekt-Typ Layer, durch den Layer-Elemente repräsentiert werden. Layer-Objekte ähneln window-Objekten (vgl. Kapitel 7), insofern jeder Layer eine eigene document-Eigenschaft besitzt. Andererseits sind Layer selbst wiederum Eigenschaften eines document-Objekts, nämlich der Repräsentation des Dokuments, in dem sie definiert wurden. Schauen wir uns noch einmal ein Beispiel an:

Beispiel

<html>
<head>
<title>zwei Dokumente</title>
<style>
#dynamo { position:absolute; left:20px; top:100px }
</style>
</head>

<body>
mein Fahrrad:
<img src="fahrrad.jpg" name="fahrradBild">

<div id="dynamo">
mein Dynamo:
<img src="dynamo.jpg" name="dynamoBild">
</div>

</body>
</html>

In der Objekt-Hierarchie ist das gesamte Dokument als window.document vertreten. Es enthält einen Layer, in Form eines absolut positionierten div-Elements. Genau wie Bilder und Formulare werden die zu einem Dokument gehörenden Layer in einem Array (genauer: einer Collection) aufgelistet. Wie Sie in Kapitel 3 gelernt haben, erhalten Sie mit document.images[0]eine Referenz auf das erste Bild einer Seite. Entsprechend verweist

document.layers[0] 

auf den ersten Layer. Und weil Collection-Elemente im Gegensatz zu den Elementen eines gewöhnlichen Arrays auch über ihren Namen (bzw. ihre ID) herausgegriffen werden können, erreichen Sie den Layer mit der ID dynamo auch über diese Ausdrücke:

document.layers["dynamo"] 
document.dynamo

Layer sind also, genau wie Image- oder Formular-Objekte, Eigenschaften von document.

Die document-Hierarchie

Nun besitzt aber wie gesagt jeder Layer sein eigenes document-Objekt. Unsere Beispielseite besteht deshalb aus zwei, ineinander verschachtelten Dokumenten:

window.document 

und

window.document.dynamo.document

Das im Layer liegende Image-Objekt "dynamoBild" finden wir folglich unter

window.document.dynamo.document.dynamoBild

Hinweis

Elemente, die in einem Layer stehen, gehören zum document-Objekt des Layers, nicht zum übergeordneten Gesamtdokument window.document.

Sollten Sie versuchen, in unserem Beispiel mit document.images[1] oder document.dynamoBild auf das Dynamo-Bild zuzugreifen, erhalten Sie deshalb von Netscape 4 nur den Wert undefined.

Layer-Eigenschaften ändern

Layer-Objekte besitzen eine Reihe von Eigenschaften, die Sie jederzeit lesen und ändern können. In Tabelle 9.3 sind die wichtigsten aufgelistet.

Layer-Eigenschaften

EigenschaftTypBeschreibung
leftNumberdie horizontale Position (in Pixeln) des Layers
topNumberdie vertikale Position des Layers
clip.topNumberdie Oberkante des sichtbaren Layer-Bereichs relativ zur Layer-Oberkante (in Pixeln). Ein Wert größer 0 schneidet also den Layer-Inhalt von oben ab.
clip.bottomNumberdie Unterkante des sichtbaren Layer-Bereichs relativ zur Layer-Oberkante
clip.leftNumberdie linke Kante des sichtbaren Layer-Bereichs relativ zur linken Layer-Kante (in Pixeln). Ein Wert größer 0 schneidet also den Layer-Inhalt von links ab.
clip.rightNumberdie rechte Kante des sichtbaren Layer-Bereichs relativ zur linken Layer-Kante.
zIndexNumberdie "Höhe" der Layer-Ebene in der Stapelreihenfolge. Layer mit hohem zIndex-Wert verdecken Layer mit niedrigerem Wert.
visibilityStringdie Sichtbarkeit des Layers. show steht für sichtbar, hide für unsichtbar, bei inherit erbt der Layer seine Sichtbarkeit vom übergeordneten Layer. Anstatt show und hide können Sie auch visible und hidden verwenden.
srcStringURL-Angabe für externe Layer-Inhalte

Tabelle 9.3: einige Eigenschaften des Layer-Objekts

DHTML besteht beim Netscape Navigator im Wesentlichen darin, diese Werte so zu ändern, dass dadurch interessante Effekte entstehen. Zum Beispiel können wir eine Animation erzeugen, indem wir regelmäßig die left- und top-Koordinaten eines Layers ändern:

Beispiel

<html>
<head>
<title>eine einfache Animation</title>
<style>
#fahrrad { position:absolute; left:-100px; top:100px }
</style>

<script>
function bewegung(){
   document.fahrrad.left+=5;
   setTimeout("bewegung()",50);
}
</script>

</head>
<body onload="bewegung()">
mein Fahrrad:
<div id="fahrrad">
   <img src="fahrrad.jpg" name="fahrradBild">
</div>
</body>
</html>

CSS-Attribute entsprechen Layer-Eigenschaften

Wie Sie vermutlich bemerkt haben, entsprechen viele der Layer-Eigenschaften bestimmten CSS-Attributen. Tatsächlich werden diese Attribute von Netscape 4 nur in Verbindung mit Layern unterstützt. Das heißt, wenn Sie einen Bereich Ihrer Seite unsichtbar machen wollen, müssen Sie ihn in einen Layer stecken, zum Beispiel in ein positioniertes div-Element. Diesem Element verpassen Sie dann die Style-Angabe "visibility:hide" (oder "visibility:hidden"). Mit

document.layerID.visibility = "show"

können Sie den Bereich später auf sichtbar schalten.

Noch mehr dynamische Möglichkeiten ergeben sich aus der Tatsache, dass die document-Objekte von Layern alle Eigenschaften und Methoden von window.document besitzen. Sie können deshalb einen Layer mit

document.layerID.document.write("einString");
document.layerID.document.close();

neu beschreiben. Damit können Sie den Inhalt eines Seitenbereichs nach dem Laden noch verändern, etwa in Reaktion auf eine Benutzer-Eingabe.

9.1.3 DHTML mit dem Internet Explorer

CSS-Eigenschaften aller HTML-Elemente sind über JavaScript zugänglich

Bei Microsoft entschloss man sich, dem DHTML-Modell von Netscape nicht zu folgen. Der Internet Explorer kennt weder den <layer>-Tag noch das Layer-Objekt und bietet auch keinen vergleichbaren Ersatz. Stattdessen sind die CSS-Eigenschaften aller HTML-Elemente über JavaScript zugänglich und veränderbar. Das verschafft dem Internet Explorer Fähigkeiten, die weit über die von Netscape 4 hinaus gehen.

Ein Beispiel:

Beispiel

<body>
"</body>

Dieser Absatz wechselt beim Überfahren mit der Maus Farbe und Schriftart. (Und dabei ist kein Link im Spiel!) Die Anweisung zur Änderung der Farbe ist:

this.style.color = 'red';

Der Ausdruck this bezieht sich, wenn er in einem Event-Handler steht, auf das Element, für das der Handler definiert ist, also hier das p-Element. Wie Sie sehen, erreicht man die CSS-Eigenschaften eines Elements in JavaScript als Eigenschaften des jedem Element zugeordneten Objekts style.

Hinweis

Wenn Sie genau hinsehen, wird Ihnen auffallen, dass die Style-Eigenschaften in JavaScript manchmal etwas anders geschrieben werden als in CSS. So wird aus font-family fontFamily. Ebenso fanden wir bei Netscape die Eigenschaft zIndex, die dem CSS-Attribut z-index entspricht. Die Regel ist einfach: Überall, wo ein Minus-Zeichen ist, wird dieses weggelassen und der nachfolgende Buchstabe groß geschrieben. Der Grund dafür ist, dass das Minus-Zeichen in JavaScript eben für Minus steht. Würden Sie also einElement.style.font-family schreiben, dann würde der JavaScript-Interpreter versuchen, den Wert des Ausdrucks family von dem des Ausdrucks einElement.style.font zu subtrahieren, wobei vermutlich nichts Sinnvolles herauskäme.

Die document.all-Collection

document.all

In der Praxis wollen wir oft die CSS-Eigenschaften eines Elements von außerhalb des Elements verändern, so dass wir nicht mit this darauf verweisen können. Wie dann? Die document.layers-Collection, in der bei Netscape 4 Layer-Elemente aufgelistet sind, existiert natürlich beim Internet Explorer nicht. Aber es gibt eine andere Collection, die uns weiterhilft. Erinnern Sie sich: Im IE sind nicht nur einige besondere HTML-Elemente dynamisch veränderbar, sondern wirklich alle. Die entsprechende Collection heißt folgerichtig document.all. Über

document.all[27]

erhalten Sie zum Beispiel das 28. HTML-Element auf der Seite (das erste, mit dem Index 0, ist das html-Element, das zweite in der Regel das head-Element, und so weiter). Die Referenzierung über Index-Nummern ist allerdings eher unhandlich: Immer wenn Sie ein neues Element in Ihre Seite einfügen, müssen alle Nummern angepasst werden. Geben Sie deshalb besser den Elementen, die Sie ansprechen wollen, ein ID-Attribut, dann können Sie mit

document.all["elementID"]

darauf zugreifen.

Wenn Sie vermuten, dass das wie bei Image- und (bei Netscape) Layer-Objekten auch mit document.elementID geht, dann haben Sie sich getäuscht. Die HTML-Elemente sind nicht als direkte Eigenschaften von document verfügbar. Es geht aber trotzdem kürzer, nämlich über

window.elementID

oder einfach elementID.

Hier noch einmal ein Rollover-Effekt, diesmal ohne "this":

<p id="par"
   onmouseover="par.style.fontSize='50pt'"
   onmouseout="document.all['par'].style.fontSize='3pt'">
ich bin sehr dynamisch</p>

Man kann, wie dieses Beispiel zeigt, auch Style-Eigenschaften ändern, die zuvor gar nicht explizit definiert wurden. Beim Lesen ist das allerdings anders:

Hinweis

Nur diejenigen style-Eigenschaften lassen sich auslesen, die inline im HTML-Tag angegeben oder dem Element nachträglich mit JavaScript zugewiesen wurden.

Das sollten Sie vor allem beachten, wenn Sie Style Sheets in eigenen <style>-Abschnitten definieren. Netscape 4 reflektiert die dort definierten Werte in den Layer-Eigenschaften, beim Internet Explorer aber werden Sie sie in den style-Eigenschaften der betroffenen Elemente nicht finden. (Seit Version 5 des Explorers gibt es als Ersatz die Eigenschaft currentStyle, die stets den aktuellen Wert enthält. Das funktioniert allerdings nur in der Windows-Version.)

Element-Inhalte ändern

Es gibt noch andere Wege, Teile einer Seite mit Internet Explorer dynamisch zu verändern. Wenn Sie zum Beispiel eine Überschrift definiert haben:

<h1 id="wau">Wau</h1>

dann können Sie diese nicht nur wann immer Sie wollen verstecken, vergrößern, verkleinern und verfärben, Sie können sogar ihren Inhalt austauschen. Dazu gibt es die Eigenschaften innerText und innerHTML, die sowohl gelesen als auch geschrieben werden dürfen:

document.all["wau"].innerText = "Miau"

verwandelt die Überschrift "Wau" in "Miau". Die Änderung ist sofort im Dokument sichtbar.

Der Unterschied zwischen innerText und innerHTML kommt zum Tragen, wenn sich innerhalb eines Elements HTML-Tags befinden. Beim body-Element ist das zum Beispiel meistens so. Wenn Sie document.body.innerText auslesen, erhalten Sie den gesamten Inhalt des body, aber nicht die darin vorkommenden HTML-Tags. document.body.innerHTML dagegen liefert Ihnen den Inhalt mitsamt den Tags.

Beispiel

Das folgende Beispiel erzeugt ein Dokument, dessen Inhalt Buchstabe für Buchstabe aufgebaut wird, ein bisschen so als würde man es gerade mit der Schreibmaschine schreiben.

<html>
<head>
<title></title>
<script>
var text = "Immerhin besitzt doch ein Mensch die Freiheit, "
   + "seine Hand auf den Kopf zu legen. (G.W. Leibniz, "
   + "Neue Abhandlung über den menschlichen Verstand, "
   + "Leipzig: Meiner 1915, S. 108)";
var zaehler = 0;
function schreib(){
   document.body.innerHTML+= text.charAt(zaehler);
   zaehler++;
   if (zaehler<text.length) setTimeout("schreib()",100);
}
</script>
</head>
<body onload="schreib()">
</body>
</html>

Dieses Beispiel gibt es wieder auf CD (beispiel0902.html).

body ist übrigens eine vordefinierte Eigenschaft von document und steht, wie der Name schon sagt, für das body-Element der Seite. Wie Sie bereits in Kapitel 7 erfuhren, erhält man darüber einige nützliche Werte. Beispielsweise liefert document.body.clientWidth die Breite des Dokuments - also ungefähr die innere horizontale Ausdehnung des Browserfensters (vgl. Anhang 2 zu Kapitel 10).

9.1.4 DHTML mit dem W3C

Das DOM

Die sehr unterschiedlichen Ansätze von Microsoft und Netscape machen es schwer, Seiten zu erstellen, die auf beiden Browsern gleichermaßen funktionieren. Die DOM-Spezifikation des World Wide Web Consortiums (W3C) soll dafür sorgen, dass das in Zukunft besser wird. "DOM" bedeutet "Document Object Model". Sie erinnern sich: Das Objektmodell bestimmt, wie die Elemente eines Dokuments (Links, Bilder, Überschriften usw.) in JavaScript als Objekte repräsentiert werden (vgl. Kapitel 3). So gesehen haben Netscape und Microsoft schon lange ihr eigenes DOM entwickelt. Wenn aber von dem DOM die Rede ist, dann ist das W3C-DOM gemeint: Es ist die offizielle, einheitliche Richtlinie für das Objektmodell zukünftiger Browser. Microsofts Internet Explorer unterstützt wesentliche DOM-Konzepte seit Version 5, und die aus dem Mozilla-Projekt hervorgegangene Version 6 von Netscape (eine Version 5 gab es nie) erfüllt die Richtlinien der ersten DOM-Spezifikation (Level 1) sogar schon so gut wie vollständig.

Hinweis

Betrachten Sie Netscape 6 nicht als Nachfolger von Version 4, sondern als eigenständige Neuentwicklung. Netscape 6 ist nicht kompatibel zum Objektmodell früherer Netscape-Versionen, insbesondere kennt er kein Layer-Objekt!

Alle in diesem Buch besprochenen W3C-Techniken sollten sowohl mit Internet Explorer 5 als auch mit Netscape 6 funktionieren.

Hinweis

Netscape 6 befindet sich derzeit (Sommer 2000) noch in einer frühen Beta-Phase. Es ist deshalb nicht völlig ausgeschlossen, dass Informationen, die Sie hier finden, für die Endversion gar nicht zutreffen. Mit großen Änderungen ist allerdings kaum zu rechnen.

Dokumente aus der Sicht des DOM

getElementById()

Das Objektmodell des W3C stellt, noch konsequenter als das von Microsoft, alle Elemente einer Webseite - bis hin zu Kommentaren - als JavaScript-Objekte zur Verfügung. Es gibt aber nicht, wie beim Internet Explorer, eine all-Collection, in der alle diese Objekte aufgelistet sind. Stattdessen besitzt das document-Objekt die Methode getElementById(), der Sie die ID eines Elements übergeben und dafür eine Referenz auf das entsprechende Objekt zurück bekommen.

Befindet sich beispielsweise irgendwo im HTML-Code die Zeile

<b id="Grundgedanke">Grundgedanke</b>

dann verweist nach der Ausführung der JavaScript-Anweisung

var grund = document.getElementById("Grundgedanke")

die Variable grund auf dieses HTML-Element.

W3C-Sprech

Beim W3C wird auf den Unterschied zwischen Elementen, Attributen und so genannten TextNodes großen Wert gelegt. Ein Element umfasst gewöhnlich einen Start-Tag (zum Beispiel <b>), einen End-Tag (</b>) und alles, was dazwischen steht. Zu den Ausnahmen gehören img- und br-Elemente: Sie besitzen keinen End-Tag und keinen Inhalt. Im Start-Tag sind häufig Attribute definiert, z.B. id="Grundgedanke" oder src="bild.jpg". TextNodes schließlich sind zwischen den HTML-Tags stehende Text-Bereiche. Das Wort "Grundgedanke" im vorigen Beispiel ist ein TextNode.

Hinweis

Bemühen Sie sich um korrektes HTML, wenn Sie mit DOM-Methoden arbeiten. Ein mit <p> begonnener Absatz muss zum Beispiel stets mit </p> wieder geschlossen werden.

Intern werden HTML-Dokumente im DOM als Baum abgebildet. Sehen wir uns zur Veranschaulichung ein vollständiges Dokument an:

<html>
  <head>
    <title>
    </title>
  </head>
  <body>
    Hier steht was.
    <p>
      Und hier kommt der
      <b id="Grundgedanke">
        Grundgedanke
      </b>
    </p>
  </body>
</html>

Stellen Sie sich das Objekt document am besten als das Stück Papier vor, auf dem der Quelltext geschrieben ist. Dieses Papier kann in ein Fenster (ein window-Objekt) gehängt (geladen) werden, woraufhin man im Fenster sehen kann, was auf dem Papier definiert ist.

Der DOM-Baum

Abb. 9.1

Abbildung 9.1: der DOM-Baum des Beispieldokuments

Das document-Objekt ist die Wurzel des Objektbaums, auf ihm ruht die ganze HTML-Struktur. Direkt über document liegt in unserem Beispiel das html-Element (es beginnt mit <html>, endet mit </html> und umfasst den gesamten Quelltext). Von da an verzweigt sich der Baum in zwei Stämme: einer fußt auf dem head-Element, der andere auf body. Man bezeichnet solche Unterglieder eines Elements auch als dessen "Kinder" (ChildNodes). body und head sind also im Beispieldokument die ChildNodes von html. head besitzt selbst wiederum ein Kind, nämlich title. Das body-Element hat bei uns zwei Kinder: das b-Element mit der ID "Grundgedanke" und den TextNode "Hier steht was". Auch TextNodes sind im DOM als JavaScript-Objekte vertreten. Der Oberbegriff für Elemente, Attribute, TextNodes und alle anderen Objekttypen im DOM ist Node ("Knoten"). Abbildung 9.1 zeigt die Nodes des Beispiel-Dokuments so wie sie im DOM repräsentiert sind.<>

Klettern auf dem DOM-Baum

Mit Hilfe von JavaScript können wir uns im DOM-Baum einer Webseite von unten nach oben, von oben nach unten und seitwärts in alle Richtungen fortbewegen. Dazu besitzt jeder Node die in Tabelle 9.4 angegebenen Eigenschaften.

Node-Eigenschaften

EigenschaftBeschreibung
firstChildder erste ChildNode in der Reihenfolge, in der sie im Quelltext definiert sind
lastChildder letzte ChildNode vor dem Schluss-Tag des Elements
childNodesein Array mit allen ChildNodes des aktuellen Elements
parentNodedas Parent-Element des Nodes
previousSiblingder vorhergehende Node auf derselben DOM-Ebene ("sibling" heißt "Geschwister".)
nextSiblingder nächste Node auf derselben DOM-Ebene

Tabelle 9.4: einige Eigenschaften von DOM-Nodes

Mit

document.body.firstChild

erhalten wir also im obigen Beispieldokument den TextNode "Hier steht was.".

document.body.firstChild.nextSibling

liefert uns das p-Element, über

document.body.firstChild.nextSibling.childNodes[1]

erreichen wir dessen zweites Kind, also das Element mit der ID "Grundgedanke". Wir hätten auch mit

document.getElementById("Grundgedanke")

direkt darauf zugreifen können. Der Ausdruck

document.getElementById("Grundgedanke").parentNode

bringt uns wieder zurück zum p-Element.

Lesen und schreiben im DOM-Baum

Die ganze Kletterei wäre nutzlos, wenn wir mit den Nodes, die wir so erreichen, nichts anfangen könnten. Das DOM erlaubt uns, Node-Inhalte zu lesen, zu überschreiben, Nodes zu löschen, ihre Attribute zu ändern und neue Nodes ins Dokument einzufügen.

Die Eigenschaft nodeValue liefert den Inhalt eines TextNodes. Hinter

document.body.firstChild.nodeValue

verbirgt sich also in unserem Beispiel der String "Hier steht was.". Sie können den Wert einfach überschreiben und dadurch den Text ändern:

document.body.firstChild.nodeValue =
   "Jetzt steht hier was anderes."

Hinweis

Beachten Sie, dass der nodeValue von Elementen stets null ist. Eine Eigenschaft wie innerText oder innerHTML, die den Inhalt eines Elements wiederspiegelt, stellt das DOM derzeit nicht zur Verfügung.

Sie können jederzeit einen neuen Node erzeugen. Dazu gibt es die Methoden

document.createElement("TagName")

und

document.createTextNode("Inhalt des Nodes")

Die Anweisung

var neuerAbsatz = document.createElement("P")

erzeugt zum Beispiel ein neues p-Element, einen Absatz also. Das Element ist anfangs noch leer und besitzt keine Attribute. Es hat auch noch keinen Platz im DOM-Baum. Um es ins Dokument einzubauen, müssen wir auf den Baum klettern und es dort befestigen. Das machen wir mit der Methode

Element.appendChild(einNode)

die den Node einNode als letztes Kind an das Element Element anhängt.

document.body.appendChild(neuerAbsatz)

würde also den vorhin erzeugten Absatz am Ende des body-Elements einfügen.

Wenn Sie einen Node nicht als letztes Kind seines ParentNodes, sondern an einer anderen Stelle einfügen wollen, benutzen Sie statt appendChild() die Methode

Element.insertBefore(einNode, nächsterNode)

wobei nächsterNode auf den Node verweist, vor dem der neue Node in den Baum eingehängt werden soll.

Jetzt ist unser Absatz also Bestandteil des Dokuments, aber immer noch leer. Um ihn zu füllen, müssen wir einen TextNode erzeugen und am neuen Absatz befestigen:

var inhalt = document.createTextNode("ein neuer Absatz");
neuerAbsatz.appendChild(inhalt);

Wir können dem neuen Element auch Attribute verpassen:

neuerAbsatz.id = "derNeueAbsatz";
neuerAbsatz.align = "right";
neuerAbsatz.style.fontWeight = "bold";

Die Auswirkung ist dieselbe, als hätten wir den Absatz in HTML so definiert:

<p id="derNeueAbsatz" align="right"
   style="font-weight:bold">

CSS-Eigenschaften im DOM

Diese Attribute kann man natürlich nicht nur schreiben, sondern auch lesen. Sie werden vor allem das style-Attribut gebrauchen, denn darüber stehen im DOM genau wie beim Internet Explorer die ganzen CSS-Eigenschaften des Elements zur Verfügung.

Hinweis

Dabei gelten dieselben Regeln wie beim Internet Explorer: Lesen kann man über die style-Eigenschaft nur die CSS-Angaben, die im Start-Tag des Elements definiert oder mit JavaScript gesetzt wurden. Wenn die Werte Maßeinheiten besitzen, dann werden diese beim Lesen mit geliefert. Es ist deshalb weder beim Internet Explorer noch im DOM möglich, ein Element mit der folgenden Anweisung um 5 Pixel nach unten zu versetzen:

document.getElementById("etwas").style.top+= 5;

Der Wert von style.top ist ein String, beispielsweise "200px". Das Ergebnis der obigen Zuweisung wäre also so etwas wie "200px5", und das ist keine erlaubte Positionsangabe. Auch die weit verbreitete Angabe von Zahlenwerten für Positions- oder Schriftgrößen ist streng genommen laut DOM nicht korrekt: Die Maßeinheit sollte nicht weggelassen werden.

Nodes können übrigens auch jederzeit aus dem DOM-Baum entfernt werden. Um unseren vorhin eingehängten Absatz wieder zu löschen, brauchen Sie nur diese Anweisung auszuführen:

document.body.removeChild(neuerAbsatz)

9.2 Übungen

Machen Sie sich keine Sorgen, wenn Sie im Grundlagen-Teil das eine oder andere nicht gleich verstanden haben. Sie werden bei den folgenden Übungen sicher noch einige Male zurückblättern müssen. Wenn Sie eine ausführliche Referenz zur Hand haben, kann es auch nicht schaden, hin und wieder einen Blick in diese zu werfen.

Die Übungen sind wie der Grundlagenteil in verschiedene Abschnitte unterteilt. Wir fangen an mit ein paar Aufgaben zu Style Sheets, die Sie überspringen können, wenn Sie damit schon Erfahrung haben. Es folgen Übungen zum DHTML-Modell von Netscape 4, dann zu dem des Internet Explorers und schließlich zum DOM des W3C. Natürlich werden Sie in der Praxis Seiten entwerfen wollen, die auf möglichst allen Browsern funktionieren. Dem Cross-Browser-DHTML ist deshalb das gesamte nächste Kapitel gewidmet. Vorher sollten Sie sich aber ein gewisses Verständnis der unterschiedlichen Ansätze von Netscape, Microsoft und dem W3C angeeignet haben.

9.2.1 Übungen mit Style Sheets

Uebung

leicht

1. Übung

Nennen Sie drei Möglichkeiten, Style Sheets in eine HTML-Seite einzubinden.

Uebung

leicht

2. Übung

Mit welcher Style-Angabe werden alle p-Elemente einer Seite in Schriftgröße 15pt formatiert?

Und was tun Sie, wenn Sie einen einzelnen Absatz von dieser Regel ausnehmen wollen?

Uebung

mittel

3. Übung

Welche CSS-Angabe müsste eigentlich der gesamten Webseite eine Breite von 500 Pixel, die Schriftart Arial und einen Abstand von 20 Pixel zum linken Fensterrand geben?

Uebung

mittel

4. Übung

Erstellen Sie eine Schrift mit einem Schatten.

9.2.2 Übungen zum DHTML-Modell von Netscape 4

Uebung

leicht

5. Übung

Hier ist eine HTML-Seite:

<html>
<head>
<title>Nichts</title>
</head>
<body>
<layer id="nichts" left="-100" top="0" width="100">
   Auf dieser Seite gibt es nichts zu sehen.
   <img src="etwas.jpg" name="etwas" width="50" height="50">
</layer>
</body>
</html>

Warum ist tatsächlich nichts zu sehen, wenn man diese Seite mit Netscape 4 lädt?

Uebung

leicht

6. Übung

Mit welcher JavaScript-Anweisung könnte man den Layer von Übung 5 in den sichtbaren Fensterbereich verschieben?

Uebung

leicht

7. Übung

Geben Sie neun verschiedene Ausdrücke an, die alle auf das Bild mit dem Namen etwas im Dokument von Übung 5 verweisen.

Uebung

mittel

8. Übung

Auf dieser Seite befindet sich ein Link, durch den man einen Textabsatz ein- und ausblenden kann:

<script>
var zustand="sichtbar";
function einaus(){
   if (zustand == "sichtbar"){
      document.vielText.visibility = "hidden";
      zustand = "unsichtbar";
   }
   else {
      document.vielText.visibility = "visible";
      zustand = "sichtbar";
   }
}
</script>

<layer id="vielText" left="200" top="20">
   Hier steht ganz viel Text
</layer>
<a href="#" onClick="einaus()">
   Text ein-/ausblenden
</a>

Schreiben Sie die Seite so um, dass darin kein Layer-Tag mehr vorkommt.

Uebung

mittel

9. Übung

Hier ein Ausschnitt aus einem anderen Dokument:

<body>
<div id="RecyclingHof"
   style="position:absolute; left:300px; top:300px;">
<div id="AltglasContainer"
   style="position:absolute; left:100px; top:-20px;">
<div id="BraunglasContainer"
   style="position:absolute; left:0px; top:30px;">
<img name="Bierflasche" src="bier.jpg">
</div>
</div>
</div>
</body>

An welcher Position, relativ zum gesamten Dokument, liegt die linke obere Ecke des Layers BraunglasContainer?

Uebung

mittel

10. Übung

Mit welcher JavaScript-Anweisung kann das Bild "bier.jpg" in Übung 9 durch "milch.jpg" ersetzt werden?

Uebung

mittel

11. Übung

Als Nächstes ein Aquarium. Erstellen Sie mit einem Grafikprogramm ein paar Bilder von Blättern und Algen und positionieren Sie sie mit Style Sheets so im Fenster, dass ein hübsches Aquarium entsteht. Wählen Sie auch eine passende Hintergrundfarbe für den body.

Es fehlen noch die Fische. Auch hier brauchen Sie geeignete Bilder, und zwar sollten Sie von jedem Fisch zwei haben: eins auf dem der Fisch nach rechts und eins auf dem er nach links schwimmt. Besonders elegant wird es, wenn Sie animierte Grafiken verwenden, dann können Sie sogar die Flossen schlagen lassen.

Lassen Sie nun Ihre Fische langsam hin und her durchs Wasser ziehen.

Uebung

schwer

12. Übung

Layer sind durchsichtig: Überall dort, wo das Dokument eines Layers keine (oder durchsichtige) Elemente enthält, ist das unter dem Layer liegende Dokument zu sehen. Um den Layer undurchsichtig zu machen, müssen Sie ihm eine Hintergrundfarbe oder ein Hintergrundbild geben. Die CSS-Angaben dafür sind:

background-image:url(dasHintergrundbild.jpg)
layer-background-image:url(dasHintergrundbild.jpg)
background-color:#6666ff;
layer-background-color:#6666ff;

Tipp

Sei sollten zusätzlich zum W3C-konformen background-color-Attribut stets auch layer-background-color angeben, denn mit background-color alleine werden nur die Layer-Bereiche farbig hinterlegt, auf denen Text oder HTML-Elemente stehen. Dasselbe gilt für background-image und layer-background-image. (Der Effekt ist nicht ganz einfach zu beschreiben - wenn Sie es ausprobieren, werden Sie sofort verstehen, was gemeint ist.)

Schreiben Sie nun eine Seite mit fünf farbigen, leicht versetzt übereinander gestapelten Quadraten.

Mit Hilfe von Formular-Buttons soll man die Stapelreihenfolge der Quadrate verändern können: Ein Button stellt die dunklen nach oben, einer die hellen, ein dritter sortiert sie nach dem Zufallsprinzip.

Um einen Layer auf quadratische Form zu strecken, benutzen Sie das CSS-Attribut clip:

"ig;e 200x200 Pixel
</div>

Eine Zufallszahl zwischen 0 und 1 liefert die Anweisung Math.random().

Uebung

mittel

13. Übung

Schicke Effekte lassen sich mit halbtransparenten Hintergrundbildern erzielen. Gemeint sind kleine Bilder im gif-Format, die gleichmäßig verteilte transparente Anteile haben (vgl. Abbildung 9.2).

Abb. 9.2

Abbildung 9.2: ein "halbtransparentes" Pixelmuster (vergrößert)

Für die nächste Aufgabe geben Sie einer Seite im body-Tag ein Hintergrundbild. Dann erstellen Sie einen Layer mit halbtransparentem Hintergrund, der einige Links enthält (der Layer natürlich, nicht der Hintergrund). Dieses Navigationsmenü soll anfangs aber nicht zu sehen sein. Erst wenn man auf einen Button klickt, erscheint es - und zwar indem es sich von oben nach unten ausfährt. Dazu ändern Sie einfach nach und nach den clip.bottom -Wert des Layers.

Benutzen Sie folgende Style-Definition:

<style type="text/css">
#menu { position:absolute; left:10; top:30;
   clip:rect(0px 200px 0px 0px); width:200px;
   background-image:url(semitrans.gif);
   padding:10px }
a { font-weight:bold; color:black;
   text-decoration:none }
</style>

Das Bild "semitrans.gif" müssen Sie natürlich erst einmal erstellen (oder von der beiliegenden CD kopieren). Der nicht-transparente Anteil sollte dabei weiß sein.

Uebung

mittel

14. Übung

Eine der Stärken des Objektmodells von Netscape liegt in der einfachen Einbeziehung externer Inhalte in ein Dokument: Im Grunde kann jeder Layer auf einer Seite von einem anderen URL stammen. Wäre dieses Konzept weiterentwickelt worden, würden wir Frames kaum noch benötigen.

Erweitern Sie die Lösung von Übung 13 so, dass bei der Auswahl eines Menüpunkts die dazugehörige Datei auf derselben Seite angezeigt wird. Auch hier soll der Hintergrund halb durchsichtig sein.

9.2.3 Übungen zum DHTML-Modell von Microsoft

Uebung

leicht

15. Übung

Wie kann man im Internet Explorer mit JavaScript auf dieses Element verweisen?

<p id="Satie">
   Das Meer ist voll Wasser - das soll einer verstehen.
</p>

Und wie jetzt?

<div id="Erik" style="position:absolute">
   <p id="Satie">
      Das Meer ist voll Wasser - das soll einer verstehen.
   </p>
</div>

Uebung

leicht

16. Übung

Sehen Sie sich dieses Stück Quelltext an:

<p id="Satie">
   Das Meer ist voll Wasser - das soll einer verstehen.
</p>
<script>
document.all["Satie"].visibility = "hidden";
</script>

Was passiert hier?

Wie könnte man erreichen, was eigentlich beabsichtigt war?

Uebung

leicht

17. Übung

Schreiben Sie eine Seite mit drei Absätzen. Die Schriftfarbe dieser Absätze soll grau sein. Bewegt man sich aber mit der Maus darüber, wechselt sie auf schwarz.

Uebung

mittel

18. Übung

Erstellen Sie eine Schrift, die ganz klein auf der Seite auftaucht und dann immer größer wird.

Uebung

mittel

19. Übung

Noch ein zoom-Effekt. Diesmal soll die Schrift nicht nur größer werden, sondern auch dunkler. Außerdem sollen mehrere Texte nacheinander eingezoomt werden, die dann untereinander stehen bleiben.

Zur Angabe der Schriftfarbe verwenden Sie am besten die Dezimal-Schreibweise:

rotesElement.style.color = "rgb(255,0,0)"

Der color-Wert ist hier ein String, bestehend aus "rgb(", dem Farbwert für Rot (0-255), gefolgt von einem Komma, gefolgt vom Grünanteil, gefolgt von noch einem Komma, dem Blauwert und einer schließenden Klammer.

Uebung

mittel

20. Übung

Auf einer Webseite steht ein Formular mit einem textarea-Feld und einem Button. Darunter befindet sich ein leeres graues Rechteck. Wenn man etwas in das Formular eingibt und den Button drückt, wird der Inhalt der textarea in das graue Rechteck kopiert. Der Witz dabei ist: Gibt man HTML-Formatierungen ein, dann werden diese im grauen Rechteck nicht angezeigt, sondern ausgewertet. Schreibt man zum Beispiel

<b>Kohl</b>

dann erscheint im Rechteck das Wort "Kohl" fett formatiert.

Schreiben Sie diese Anwendung.

Uebung

mittel

21. Übung

Sollten Sie bereits versucht haben, auch im Internet Explorer mit halbtransparenten Hintergrundbildern zu arbeiten, dann wird Ihnen nicht entgangen sein, dass IE zum Rendern hierfür wesentlich länger braucht als Netscape - zu lange selbst für einfache Animationen.

CSS-Filter

Das ist aber nicht weiter schlimm, denn Internet Explorer kennt - zumindest in der Windows-Version - etwas viel Schöneres: CSS-Filter. Um ein positioniertes Element halb durchsichtig zu machen, brauchen Sie ihm lediglich die CSS-Angabe filter:alpha(opacity=50) zu geben:

"; 

Der Wert 50 liegt genau in der Mitte zwischen unsichtbar (0) und undurchsichtig (100).

Wie alle style-Angaben lässt sich auch die "Filterstärke" mit JavaScript verändern. Schreiben Sie eine Seite, deren gesamter Inhalt nach dem Laden langsam eingefadet wird.

Uebung

schwer

22. Übung

In Übung 14 sahen Sie, wie man bei Netscape 4 mit Hilfe von Layern eine externe HTML-Seite nachträglich in ein bestehendes Dokument einfügen kann. Der Internet Explorer kennt kein Layer-Objekt. Wie könnte man den gewünschten Effekt trotzdem erreichen?

9.2.4 Übungen zum W3C DOM

Uebung

leicht

23. Übung

Zeichnen Sie den DOM-Baum des folgenden Dokuments.

<html>
<head>
<title>Wo</title>
</head>
<body>
<h2>Wo kämen wir hin...</h2>
<p>...wenn jeder sagte: <b>Wo kämen wir hin?</b>,<br>
und keiner ginge, um zu sehen, wohin wir kämen,
wenn wir gingen?</p>
</body>
</html>

Vergessen Sie nicht, dass auch TextNodes im DOM eigene Objekte sind!

Uebung

leicht

24. Übung

Im Dokument der vorherigen Übung: Worauf verweisen diese beiden Ausdrücke?

document.body.childNodes[0].firstChild
document.childNodes[0].lastChild.firstChild.
childNodes[0]

Uebung

leicht

25. Übung

Und worauf zeigt dieses Ungetüm?

document.body.previousSibling.lastChild.parentNode.
parentNode.childNodes[1].childNodes[1].previousSibling.
lastChild

Gehen Sie Schritt für Schritt der Referenzierung durch den DOM-Baum nach.

Uebung

leicht

26. Übung

Wieso löschen diese Anweisungen nicht den Inhalt der Überschrift?

var el = document.body.childNodes[0].firstChild;
document.body.removeChild(el);

Uebung

mittel

27. Übung

Schreiben Sie eine Funktion, die, wenn Sie onload aufgerufen wird, alle Überschriften der Seite automatisch durchnummeriert. Das heißt, wenn die erste Überschrift "Der Treibhauseffekt" heißt, dann soll daraus "1. Der Treibhauseffekt" werden, und so weiter.

Gehen Sie der Einfachheit halber davon aus, dass alle Überschriften direkte Kinder des body-Elements sind, so dass Sie den DOM-Baum nicht weiter zu durchsuchen brauchen. Sie müssen allerdings herausfinden, ob es sich bei einem bestimmten Node um eine Überschrift handelt. Dabei hilft Ihnen die Element-Eigenschaft tagName, die den Tag-Namen in Großbuchstaben enthält, also zum Beispiel "H1".

Uebung

schwer

28. Übung

Verbessern Sie die Funktion aus der letzten Übung, so dass sie nicht nur die Überschriften durchnummeriert, sondern zusätzlich am Anfang der Seite ein kleines Inhaltsverzeichnis einfügt, das mit lokalen Links auf die einzelnen Unterabschnitte verweist. Die Einträge im Inhaltsverzeichnis sollen dem Text der dazugehörigen Überschriften entsprechen. !! IE5/Mac Entity-Probleme??!!!

Uebung

schwer

29. Übung

Schreiben Sie eine JavaScript-Funktion, die ein neues Fenster öffnet und darin den DOM-Baum des Ausgangsfensters darstellt. Die Ausgabe könnte zum Beispiel so aussehen:

+ HTML
|
+--+ HEAD
|  |
|  +--+ TITLE
|     |
|     +-- ein Titel
|
+--+ BODY
   |
   +-- vergessen Sie nie den
   |
   +--+ B
      |
      +-- Grundgedanken

Uebung

leicht

30. Übung

Benutzen Sie das Skript aus der letzten Übung, um sich anzusehen, wie die neuesten Browser den DOM-Baum verschiedener Seiten intern abbilden. Dazu können Sie das Bookmark von der CD benutzen oder den Quelltext, den Sie testen wollen, einfach zusammen mit dem Script auf eine Seite stellen.

Sie werden einige Besonderheiten entdecken, die Sie bei der Arbeit mit DOM-Methoden beachten sollten.

9.3 Tipps

Tipp zu 4:

Tipps zu 11:

Tipps zu 12:

Tipps zu 14:

Tipp zu 19:

Tipp zu 20:

Tipps zu 21:

Tipps zu 22:

Tipps zu 27:

Tipps zu 28:

Tipps zu 29:

Hinweis

Sonderzeichen im DOM

9.4 Lösungen

Lösung zu 1:

  1. CSS-Eigenschaften können direkt im HTML-Tag des betroffenen Elements mit Hilfe des style-Attributs definiert werden.
  2. CSS-Angaben können in einem eigenen style-Element im head der Seite stehen.
  3. Um Formate für mehrere Seiten zu nutzen, können diese auch als externe Dateien eingebunden werden. Dazu gibt es den link-Tag.

Lösung zu 2:

Wir legen in einem style-Element die Schriftgröße aller Absätze auf 15pt fest:

p { font-size: 15pt }

Den einen Absatz, der anders dargestellt werden soll, versehen wir mit einer ID und weisen ihm so eine andere Schriftgröße zu:

#wichtigerAbsatz { font-size: 25pt }        

Lösung zu 3:

body { width:520px; padding:20px; font-family:Arial }

So jedenfalls sollte es funktionieren. Wenn Sie es ausprobieren, werden Sie aber bemerken, dass sich hier bei Netscape und Internet Explorer Bugs eingeschlichen haben: Der Internet Explorer ignoriert die width-Angabe, bei Netscape 4 geht häufig die Schriftart verloren, zum Beispiel in und nach einer Tabelle.

Hinweis

Auf solche Bugs werden Sie noch häufig stoßen. Es empfielt sich deshalb, CSS-Angaben immer sofort mit den Browsern zu testen, um sich später eine mühsame Fehlersuche zu ersparen.

Lösung zu 4:

Die Style-Definition für die beiden Elemente könnte so aussehen:

<style type="text/css">
#schrift { position:absolute; left:25px; top: 15px;
           color:#3333ff; font-size:30px; z-index:20; }
#schatten { position:absolute; left:27px; top: 18px;
            color:#bbbbbb; font-size:30px; z-index:10; }
</style>

Im body brauchen nur noch die beiden Elemente angelegt zu werden:

<div id="schrift">Wo Schatten ist, ist Licht.</div>
<div id="schatten">Wo Schatten ist, ist Licht.</div>

Lösung zu 5:

Der gesamte Inhalt des body steht in einem Layer. Die linke Kante des Layers ist auf -100 Pixel gesetzt, das heißt auf 100 Pixel links vom linken Rand des Browserfensters. Da außerdem die Layerbreite auf 100 Pixel beschränkt ist, liegt der ganze Layer außerhalb des sichtbaren Bereichs.

Lösung zu 6:

Die horizontale Position des Layers ist in der left-Eigenschaft des Layer-Objekts festgelegt. Um die Layerposition zu verändern, brauchen Sie diesen Wert nur zu überschreiben:

document.layers["nichts"].left = 0;

Jetzt befindet sich der Layer genau in der linken oberen Ecke des Fensters.

Lösung zu 7:

Die neun Ausdrücke, die Sie gefunden haben sollten, sind:

document.layers[0].document.images[0]
document.layers[0].document.images["etwas"]
document.layers[0].document.etwas
document.layers["nichts"].document.images[0]
document.layers["nichts"].document.images["etwas"]
document.layers["nichts"].document.etwas
document.nichts.document.images[0]
document.nichts.document.images["etwas"]
document.nichts.document.etwas

Es gibt sogar noch mehr. Zum Beispiel können Sie auf Collection-Elemente auch zugreifen, indem Sie das Element als Eigenschaft der Collection ansprechen:

document.layers.nichts.document.images.etwas

Was dagegen nicht funktioniert, ist:

document.images[0]
document.images["etwas"]
document.etwas

Lösung zu 8:

Die Faustregel lautet: Alle <layer> durch <div> mit absoluter CSS-Positionierung ersetzen. Da positionierte div-Elemente von JavaScript als Layer behandelt werden, ändert sich am Skript-Bereich überhaupt nichts. Nur der Layer selbst wird anders geschrieben:

<div id="vielText"
   style="position:absolute; left:200px; top:20px">
   Hier steht ganz viel Text
</div>

Lösung zu 9:

Die Positionsangaben im Style-Attribut der div-Tags werden (genau wie die in einem layer-Tag) immer relativ zu demjenigen Dokument ausgewertet, in dem das div-Element sich befindet. Die Beispielseite enthält vier Dokumente: Das Dokument des BraunglasContainers liegt innerhalb des AltglasContainer-Dokuments, dieses wiederum liegt innerhalb des RecyclingHofs, welcher sich schließlich im Hauptdokument befindet. Um die Position des BraunglasContainers zu bestimmen, müssen Sie die Koordinaten zusammenzählen: Seine linke Kante ist 0px entfernt von der linken Kante des BraunglasContainers, diese Kante ist 100px rechts von der des RecyclingHofs, welche 300px von der linken Fensterkante entfernt liegt. Die resultierende x-Koordinate ist also 400px. Die y-Koordinate ergibt, wie Sie leicht nachprüfen können, 310px.

Lösung zu 10:

Zum Beispiel mit

document.RecyclingHof.document.AltglasContainer.document.
BraunglasContainer.document.Bierflasche.src = "milch.jpg";

Wenn Ihnen Übung 7 Spaß gemacht hat, können Sie jetzt die 255 anderen Möglichkeiten lien, wie man eine Referenz auf das Bild erhalten kann.

Lösung zu 11:

Zuerst verteilen wir ein paar Pflanzen:

<body bgcolor="#4077a9">
<div style="position:absolute; left:-10; top:200;
   z-index:20">
<img src="alge.gif" width="50" height="250">
</div>
<div style="position:absolute; left:100; top:300;
   z-index:30">
<img src="alge.gif" width="30" height="200">
</div>
<div style="position:absolute; left:600; top:250;
   z-index:10">
<img src="alge.gif" width="50" height="350">
</div>
<div style="position:absolute; left:200; top:250;
   z-index:40">
<img src="blatt.gif" width="50" height="90">
</div>
<div style="position:absolute; left:550; top:350;
   z-index:20">
<img src="blatt.gif" width="60" height="100">
</div>

Wie Sie sehen, war ich faul und habe nur zwei Grafiken erstellt, die ich in verschiedene Größen skaliere. Genauso mache ich es bei den Fischen:

<div id="fisch1"
   style="position:absolute; left:0; top:220; z-index:35">
<img src="fischre.gif" name="f1" width="150" height="75">
</div>
<div id="fisch2"
   style="position:absolute; left:800; top:350; z-index:15">
<img src="fischli.gif" name="f2" width="100" height="50">
</div>

Jetzt müssen wir die Fische animieren. Dazu brauchen wir JavaScript.

Es gibt zahlreiche Herangehensweisen für Animations-Effekte. Hier ein ganz einfacher Vorschlag. Im Laufe dieses und des nächsten Kapitels werden Ihnen noch elegantere Lösungen begegnen.

Erst einmal halten wir die aktuelle Bewegungsrichtung unserer Fische fest:

var richtung_fisch1 = "rechts";
var richtung_fisch2 = "links";

Dann brauchen wir eine Funktion, die die Fische um einen Schritt vorwärts bewegt und gegebenenfalls die Richtung wechselt:

function bewegeFische(){
   if (richtung_fisch1=="rechts") {
      // Fisch schwimmt nach rechts
      if (document.fisch1.left<800)
         // 3 Pixel vorwärts
         document.fisch1.left+=3;
      else {
         // wenden
         richtung_fisch1 = "links";
         document.fisch1.document.f1.src="fischli.gif";
      }
   }
   else {
      // Fisch schwimmt nach links
      if (document.fisch1.left>100)
         document.fisch1.left-=3;
      else { // wenden
         richtung_fisch1 = "rechts";
         document.fisch1.document.f1.src="fischre.gif";
      }
   }
   // Dasselbe fuer den zweiten Fisch:
   if (richtung_fisch2=="rechts") {
      if (document.fisch2.left<600)
         // dieser Fisch ist etwas langsamer:
         document.fisch2.left+=2;
      else {
         richtung_fisch2 = "links";
         document.fisch2.document.f2.src="fischli.gif";
      }
   }
   else {
      if (document.fisch2.left>0)
         document.fisch2.left-=2;
      else {
         richtung_fisch2 = "rechts";
         document.fisch2.document.f2.src="fischre.gif";
      }
   }
}

Mit setInterval rufen wir diese Funktion alle 50 Millisekunden auf:

setInterval("bewegeFische()",50);

Tipp

Für flüssige Bewegungen sollten Sie stets einen Zeitabstand von 30-50 Millisekunden wählen. Ein gewisses Ruckeln ist dabei leider immer noch zu erkennen. Niedrigere Werte können die Browser aber (zumindest auf Windows-Betriebssystemen) nicht verarbeiten. Nur auf MacIntosh und Unix/Linux sind wirklich weiche Bewegungsabläufe möglich.

Lösung zu 12:

Wir schreiben erst einmal die Layer. So sieht der erste aus:

<div id="q1"
   style="position:absolute; left:200px; top:200px;
   clip:rect(0px 200px 200px 0px); z-index:1;
   background-color:#000099;
   layer-background-color:#000099">
Quadrat 1
</div>

Um die Stapelreihenfolge festzulegen, habe ich ihm neben einer Position und einer Hintergrundfarbe einen festen z-index-Wert zugewiesen. Lässt man bei einer Layer-Definition die z-index-Angabe weg, dann gibt Netscape dem Layer automatisch den z-index-Wert des zuletzt erzeugten Layers. Bei gleichem z-index-Wert liegt der zuletzt definierte Layer oben.

Die anderen Quadrate sehen ähnlich aus, wir ändern einfach Namen, Position, Farbe und z-index:

<div id="q2"
   style="position:absolute; left:210px; top:190px;
   clip:rect(0px 200px 200px 0px); z-index:2;
   background-color:#0033cc;
   layer-background-color:#0033cc">
Quadrat 2
</div>
<div id="q3"
   style="position:absolute; left:220px; top:180px;
   clip:rect(0px 200px 200px 0px); z-index:3;
   background-color:#0066ff;
   layer-background-color:#0066ff">
Quadrat 3
</div>
<div id="q4"
   style="position:absolute; left:230px; top:170px;
   clip:rect(0px 200px 200px 0px); z-index:4;
   background-color:#3399ff;
   layer-background-color:#3399ff">
Quadrat 4
</div>
<div id="q5"
   style="position:absolute; left:240px; top:160px;
   clip:rect(0px 200px 200px 0px); z-index:5;
   background-color:#66ccff;
   layer-background-color:#66ccff">
Quadrat 5
</div>

Als Nächstes brauchen wir ein Formular zur Steuerung der Stapelreihenfolge:

<form name="steuerung">
<input type="button"
       value="hell nach oben"
       onclick="hellhoch()">
<br>
<input type="button"
       value="dunkel nach oben"
       onclick="dunkelhoch()">
<br>
<input type="button"
       value="mischen"
       onclick="mischen()">
</form>

Jetzt fehlen nur noch die JavaScript-Funktionen. Diese sind ganz einfach:

function hellhoch() {
   for (i=1;i<=5;i++) {
      document.layers["q"+i].zIndex=i;
   }
}

function dunkelhoch() {
   for (i=1;i<=5;i++) {
      document.layers["q"+i].zIndex=6-i;
   }
}

function mischen() {
   for (i=1;i<=5;i++) {
      document.layers["q"+i].zIndex=Math.random()*10;
   }
}

Tipp

Wir machen uns hier die Tatsache zu Nutze, dass beim Verweisen auf Layer mittels

document.layers["LayerID"]

der Layername als String angegeben wird. Deshalb können wir innerhalb der eckigen Klammern String-Operationen ausführen und Variablen einsetzen. Mit anderen Schreibweisen wie

document.LayerID

wäre das nicht möglich. Zusammen mit den strategisch günstig gewählten Layernamen erhalten wir so angenehm schlanke Funktionen.

Lösung zu 13:

Nachdem die CSS-Angaben im style-Bereich untergebracht sind, sieht das Menü wie folgt aus:

<div id="menu">
<a href="eins.html">eine andere Seite</a><br>
<a href="zwei.html">ein anderes Buch</a><br>
<a href="drei.html">ein anderes Papier</a><br>
<a href="vier.html">eine andere Erde</a><br>
<a href="fuenf.html">eine andere Welt</a>
</div>

Dazu kommt der einfache Button, der die JavaScript-Funktion aufruft:

<form name="steuerung">
<input type="button"
       value="Menü anzeigen"
       onclick="menuEinAus()">
</form>

Die Funktion will erst einmal wissen, ob das Menü im Moment aus- oder eingeklappt ist. Deshalb definieren wir eine globale Variable, die den aktuellen Menüzustand speichert:

var zustand = "versteckt";

Die Funktion selbst sollte Ihnen keine allzu großen Schwierigkeiten bereiten, sie entspricht weitgehend den Funktionen zum Bewegen der Fische im Aquarium. Mein Vorschlag sieht diesmal so aus:

function menuEinAus() {
   if (zustand == "versteckt") {
      document.menu.clip.bottom+=5;
      if (document.menu.clip.bottom>150) {
         zustand = "sichtbar";
         return;
      }
   }
   else {
      document.menu.clip.bottom-=5;
      if (document.menu.clip.bottom<1) {
         zustand = "versteckt";
         return;
      }
   }
   setTimeout("menuEinAus()",30);
}

Diesmal habe ich die return-Anweisung eingesetzt: Wenn die Bewegung an ihrem Ziel angelangt ist, wird damit die Abarbeitung der Funktion beendet.

Lösung zu 14:

Sie brauchen zwei ineinander verschachtelte Layer hintergrund und inhalt:

<div id="hintergrund">
   <div id="inhalt">
   </div>
</div>

Die Style-Definitionen aus Übung 13 muss dazu um die folgenden Einträge ergänzt werden:

#hintergrund { position:absolute; left:250; top:30;
   clip:rect(0px 500px 500px 0px);
   layer-background-image:url(semitrans.gif) }
#inhalt { position:absolute; left:10; top:10 }

In der JavaScript-Funktion, die den Inhalt von inhalt wechselt, können Sie entweder mit src oder mit load arbeiten. Die erste Variante sieht so aus:

function lade(url) {
   document.hintergrund.document.inhalt.src = url;
}

Wir nehmen aber besser load, um dadurch die Layer-Breite bestimmen zu können. Außerdem wollen wir, dass nach der Auswahl eine Menüpunktes das Menü wieder zuklappt. Deshalb wird noch menuEinAus() aufgerufen:

function lade(url) {
   document.hintergrund.document.inhalt.load(url,480);
   menuEinAus();
}

Anschließend müssen nur noch die Menü-Einträge angepasst werden, damit sie die Funktion lade() aufrufen:

<div id="menu">
<a href="javascript:lade('eins.html')">Seite</a><br>
<a href="javascript:lade('zwei.html')">Buch</a><br>
<a href="javascript:lade('drei.html')">Papier</a><br>
<a href="javascript:lade('vier.html')">Erde</a><br>
<a href="javascript:lade('fuenf.html')">Welt</a>
</div>

Lösung zu 15:

Mit

document.all["Satie"]

oder

document.all.Satie

oder

window.Satie

oder

Satie

Ob sich das Element in einem div befindet (positioniert oder nicht), ist dem Internet Explorer ziemlich egal.

Lösung zu 16:

Was passiert ist Folgendes: Dem Objekt document.all.Satie wird eine neue Eigenschaft verpasst, nämlich visibility, und dieser Eigenschaft wird als Wert der String "hidden" zugewiesen.

Sonst passiert nichts.

Um das Satie-Element unsichtbar zu machen, muss die Eigenschaft visibility seines style-Objekts geändert werden:

document.all["Satie"].style.visibility = "hidden";

Lösung zu 17:

<p style="color:gray"
   onmouseover="this.style.color='black'"
   onmouseout="this.style.color='gray'">
... erster Absatz ...
</p>
<p style="color:gray"
   onmouseover="this.style.color='black'"
   onmouseout="this.style.color='gray'">
... zweiter Absatz ...
</p>
<p style="color:gray"
   onmouseover="this.style.color='black'"
   onmouseout="this.style.color='gray'">
... dritter Absatz ...
</p>

Lösung zu 18:

Zuerst brauchen wir ein Element, in dem der Text steht. Anfangs hat dieser eine sehr kleine Schriftgröße:

<div id="zoomDiv" align="center" style="font-size:2px">
der Zoom-Text
</div>

Wenn die Seite fertig geladen ist, wird eine JavaScript-Funktion aufgerufen, die die Schrift vergrößert:

<body onload="zoom()">

Um nicht jedesmal die aktuelle Schriftgröße auslesen zu müssen, legen wir eine Variable an, die den derzeitigen Wert enthält:

var schriftGroesse = 2;

Bei jedem Funktionsaufruf wird die Schrift um 5 Pixel vergrößert. Wenn 200px erreicht sind, bricht die Funktion ab, andernfalls ruft sie sich mit 30 Millisekunden Verzögerung erneut auf.

function zoom() {
   schriftGroesse+=5;
   zoomDiv.style.fontSize = schriftGroesse+"px";
   if (schriftGroesse<200) setTimeout("zoom()",30);
}   

Wenn Sie das Beispiel ausprobiert haben, konnten Sie vermutlich (wenn Sie nicht gerade eine Kinoleinwand als Bildschirm verwenden) feststellen, wie der Internet Explorer das Dokument ständig neu rendert: Er fügt zum Beispiel wenn nötig Zeilenumbrüche ein, um den Text im Fenster unterzubringen.

Lösung zu 19:

Drei Textbereiche sollen eingezoomt werden:

<div id="zoomDiv1" align="center"
   style="font-size:2px; color:#ffffff">
Kein Angst!
</div>
<div id="zoomDiv2" align="center"
   style="font-size:2px; color:#ffffff">
Es ist nur
</div>
<div id="zoomDiv3" align="center"
   style="font-size:2px; color:#ffffff">
DHTML...
</div>

Für die zoom-Funktion brauchen wir diesmal neben der Größen-Variable auch eine Variable, die den aktuellen Grauwert speichert. Am Anfang ist er 255, die Schrift also weiß:

var schriftGroesse = 2;
var farbe = 255;

Jetzt könnte man einfach drei Funktionen schreiben, von denen die erste den ersten div-Bereich einzoomt, wenn sie fertig ist, die zweite aufruft, die den zweiten einzoomt, und so weiter. Es geht aber kompakter, indem das aktuelle div-Element ebenfalls in einer Variablen mitgezählt wird. Die Referenzierung auf das Element geht dann so:

document.all["zoomDiv"+divZaehler]

Hier die vollständige Funktion:

var divZaehler = 1;

function zoom(){
   schriftGroesse += 5;
   // jeder Absatz wird in 20 Schritten gezoomt, deshalb
   // machen wir die Schrift bei jedem Schritt um 255/20
   // dunkler, so dass wir am Schluss bei 0 ankommen:
   farbe -= 255/20;
   var el = document.all["zoomDiv"+divZaehler];
   el.style.fontSize = schriftGroesse+"px";
   el.style.color = "rgb("+farbe+","+farbe+","+farbe+")";
   if (schriftGroesse>100) { // ab in die nächste Runde
      divZaehler++;
      schriftGroesse = 2;
      farbe = 255;
   }
   if (divZaehler<4) setTimeout("zoom()",30);
}

Lösung zu 20:

Zuerst das Formular mit dem Eingabefeld:

<form name="formular">
<textarea name="eingabe" cols="50" rows="10">
</textarea>
<input type="button" value="anzeigen"
   onclick="schreib()">
</form>

Dann das graue Rechteck:

<div id="rechteck"
   style="position:absolute; left:10px; top:200px;
   width:400; height:300; background-color:#cccccc;">
</div>

(Beachten Sie, dass bei Internet Explorer keine clip-Angabe nötig ist, um die Fläche des div zu erweitern, width und height reichen aus. Das Attribut layer-background-color gibt es natürlich auch nicht.)

Schließlich die verblüffend simple JavaScript-Funktion:

function schreib() {
  document.all.rechteck.innerHTML =
     document.forms['formular'].eingabe.value;
}

Sie können den "HTML-Editor"-Effekt übrigens noch verbessern, indem Sie den Button weglassen und stattdessen im textarea-Tag einen onkeyup-Handler einsetzen:

<textarea name="eingabe"
   cols=50 rows=10
   onkeyup="schreib()">
</textarea>

Damit wirkt sich jede Eingabe ins Formularfeld automatisch im Anzeige-Rechteck aus.

Lösung zu 21:

Ich wende den Filter auf das body-Element an:

<html>
<head>
<title>gar nicht fade</title>
<script>
var z = 0;
function fadein(){
   z+=5;
   document.body.style.filter = "alpha(opacity:"+z+")";
   if (z<100) setTimeout("fadein()",50);
}
</script>
</head>
<body style="position:absolute; filter:alpha(opacity:0)"
   onload="fadein()">

...der Seiteninhalt...

</body>
</html>

Hinweis

Für einfache Effekte wie Ein-, Aus- und Überblenden bietet Microsoft vorgefertigte dynamische Filter an. Das folgende Beispiel zeigt einen eleganten, mit Hilfe des blendTrans-Filters realisierten Mouseover-Effekt:

<html>
<head>
<script>
function rollover() {
   document.all.bild.filters.blendTrans.Apply();
   document.all.bild.src = "bildOver.gif";
   document.all.bild.filters.blendTrans.Play();
}
function rollout(){
   document.all.bild.filters.blendTrans.Apply();
   document.all.bild.src = "bild.gif";
   document.all.bild.filters.blendTrans.Play();
}
</script>
</head>
<body>
<img src="bild.gif" id="bild"
   style="filter:blendTrans(Duration=0.5)"
   onmouseover="rollover()" onmouseout="rollout()">
</body>
</html>

Dynamische Filter funktionieren derzeit nur mit Internet Explorer auf Windows, und auch hier nicht in allen Versionen: Manche IE 4 ignorieren sie völlig, und selbst IE 5.5 versteht viele Filtereffekte nicht mehr (- dafür kennt er ein paar Neue).

Lösung zu 22:

Im HTML-Quelltext der Hauptseite steht ein Element, in dem das externe Dokument angezeigt werden soll:

<div id="anzeige" style="background-color:#dddddd;
   width:400px; height:400px">
</div>

Dann brauchen wir einen Iframe, der den Seiteninhalt zwischenspeichert. Mit geeigneten Style-Angaben wird er unsichtbar:

<iframe id="puffer" style="width:0px; height:0px">
</iframe>

Um die externe Seite einzubinden, wird die Funktion lade() aufgerufen:

<a href="javascript:lade('eins.html')">
eine andere Seite
</a>

Diese Funktion tut nichts anderes, als die übergebene Adresse in den Iframe zu laden:

function lade(url) {
  puffer.location.href = url;
}

Schließlich brauchen wir noch eine Funktion, die aufgerufen wird, wenn der Iframe-Inhalt fertig geladen ist. Dann nämlich kann der Inhalt des Iframe-body ausgelesen und in den anzeige-div kopiert werden:

function fertig(){
   document.all.anzeige.innerHTML =
      puffer.document.body.innerHTML;
}

Was noch fehlt, ist nur ein entsprechender Eintrag im body-Tag der externen Seite:

<body onload="parent.fertig()">

Das war's.

Hinweis

In Version 5 des Internet Explorers gibt es noch einen besseren Weg, der auf einem DHTML-Behavior beruht. Behaviors werden derzeit von Netscape 6 und der Windows-Version des IE5 unterstützt (das Format ist allerdings zwischen beiden sehr unterschiedlich). Es handelt sich dabei um externe Komponenten, die das Standardverhalten bestimmter HTML-Elemente festlegen. Bei Microsoft gibt eine Reihe von Default-Behaviors, die jedoch keine Extra-Datei verlangen. Dazu gehört auch das Download-Behavior. Die Syntax ist ganz einfach:

Wir nehmen irgendein Element und verpassen ihm das Behavior:

<p id="schaufel"
   style="behavior:url(#default#download)">
</p>

(Bei gewöhnlichen Behaviors würde man dort, wo hier #default#download steht, den URL der externen Datei eintragen.)

Damit besitzt das Element automatisch die Methode startDownload(), der zwei Parameter übergeben werden: Eine einzulesende (Text-)Datei und eine Funktion, die aufgerufen wird, wenn die Datei fertig gelesen ist. Dieser Funktion wird dann der Datei-Inhalt automatisch als String übergeben. Für unser Beispiel sähe das so aus:

schaufel.startDownload('eins.html', fertig);
function fertig(text) {
   document.all.anzeige.innerHTML = text;
}

Lösung zu 23:

Abb. 9.3

Abbildung 9.3: der DOM-Baum von Übung 23

Lösung zu 24:

Beide Ausdrücke zeigen auf den TextNode "Wo kämen wir hin...".

document.childNodes[0].lastChild entspricht auf der Beispielseite document.body, und "Wo kämen wir hin..." ist das erste Kind des ersten Kindes (das h2-Element) des body. Das erste Kind eines Elements erreicht man wahlweise mit firstChild oder childNodes[0].

Lösung zu 25:

Sie ahnten es: Auch dieser Ausdruck verweist auf "Wo kämen wir hin...". Schauen wir uns die einzelnen Schritte an: Wir starten mit

document.body.previousSibling

beim head-Element, machen mit

lastChild.parentNode

von dort einen nutzlosen Abstecher zum title und zurück. Über

parentNode

landen wir beim Eltern-Element des head, also bei html, von da an geht es mit

childNodes[1].childNodes[1]

über den body zum TextNode "...wenn jeder sagte: ". Dessen vorangehender Node,

previousSibling

ist das h2-Element.

lastChild

führt uns schließlich zu dessen einzigem Kind: "Wo kämen wir hin...".

Lösung zu 26:

Die Methoden zum Anhängen und Entfernen von Nodes beziehen sich immer auf die Kinder des Elements, von dem aus Sie aufgerufen werden. (Daher das "child" in appendChild und removeChild.) Der Inhalt der Überschrift ist aber ein Kind des h2-Elements, nicht von document.body. Korrekt müsste es also heißen:

var el = document.body.childNodes[0].firstChild;
document.body.childNodes[0].removeChild(el);

oder:

var el = document.body.childNodes[0].firstChild;
el.parentNode.removeChild(el);

Lösung zu 27:

function ordnung() {
   // In einem Array speichern wir die zu überprüfenden
   // Tags:
   var tags = new Array("H1","H2","H3","H4","H5");

   // Dann brauchen wir eine Variable, die die Überschriften
   // zählt:
   var zaehler = 1;

   // Jetzt durchsuchen wir die Kinder von body
   // und ändern den Inhalt aller Überschriften:
   for (i=0; i<document.body.childNodes.length; i++){
      kind = document.body.childNodes[i];
      for (j=0; j<tags.length; j++){
         if (kind.tagName == tags[j]) {
            kind.firstChild.nodeValue =
               zaehler+". "+kind.firstChild.nodeValue;
            zaehler++;
         }
      }
   }
} 

Lösung zu 28:

function ordnung(){
   // Zuerst die globalen Variablen, angefangen mit dem
   // Überschriften-Zähler und dem Array für die gesuchten
   // Tags:
   var tags = new Array("H1","H2","H3","H4","H5");
   var zaehler = 1;

   // Wir brauchen außerdem einen Array für die Texte der
   // Überschriften, aus denen wir das Inhaltsverzeichnis
   // aufbauen:
   var inhalt = new Array();

   // Als Nächstes durchsuchen wir die Kinder des body
   // nach Überschriften, geben ihnen IDs, benennen ihren
   // Inhalt um und sammeln ihn im Array inhalt:
   for (i=0; i<document.body.childNodes.length; i++){
      kind = document.body.childNodes[i];
      for (j=0; j<tags.length; j++){
         if (kind.tagName == tags[j]) {
            kind.firstChild.nodeValue =
               zaehler+". "+kind.firstChild.nodeValue;
            kind.id="absatz"+zaehler;
            inhalt[inhalt.length] =
               kind.firstChild.nodeValue;
            zaehler++;
         }
      }
   }

   // Jetzt erstellen wir den div für's Inhaltsverzeichnis
   // und dekorieren ihn ein bisschen:
   var inhaltEl = document.createElement("div");
   document.body.insertBefore(inhaltEl,
      document.body.firstChild);
   inhaltEl.style.padding = "10px";
   inhaltEl.style.backgroundColor = "#eeeeee";
   inhaltEl.style.width = "400px";

   // Schließlich stellen wir dort für jede Überschrift
   // einen Link hinein:
   for (i=0; i<inhalt.length; i++) {
      var punktLink = document.createElement("a");
      punktLink.href = "#absatz"+(i+1);
      var punktTxt = document.createTextNode(inhalt[i]);
      punktLink.appendChild(punktTxt);
      inhaltEl.appendChild(punktLink);
      // hinter jeden Link einen Zeilenumbruch:
      var punktBr=document.createElement("br");
      inhaltEl.appendChild(punktBr);
   }
}

Lösung zu 29:

// Neues Fenster öffnen:
popup=open("","ausgabeFenster");

// pre-Element für den Baum erzeugen:
var pd = popup.document;
var baum = pd.createElement("PRE");
pd.body.appendChild(baum);

// unterste DOM-Ebene schreiben:
for (var i=0; i<document.childNodes.length; i++){
   var kind = document.childNodes[i];
   var el = pd.createElement("DIV");
   if (document.childNodes[i].nodeType==1){
      var inh = pd.createTextNode("+ "+kind.tagName);
      el.appendChild(inh);
      baum.appendChild(el);
      holeKinder("",document.childNodes[i]);
   }
}

function holeKinder(plusminus, element){
   for (var i=0; i<element.childNodes.length; i++){

      // Erst einmal den Baum um eine Zeile weitermalen.
      // Die Reihe von Strichen und Plus-Zeichen, die
      // links von denen der aktuellen DOM-Ebene stehen,
      // wurde als Parameter plusminus übergeben.
      var el = pd.createElement("DIV");
      var inh = pd.createTextNode(plusminus+"|");
      el.appendChild(inh);
      baum.appendChild(el);

      // Dann schreiben wir die Inhalte der TextNodes und
      // die Tag-Namen der Elemente dieser Ebene:
      var kind = element.childNodes[i];
      var el = pd.createElement("DIV");
      if (element.childNodes[i].nodeType==1){
         var neuInh = plusminus + "+--+ " + kind.tagName;
         var inh = pd.createTextNode(neuInh);
         el.appendChild(inh);
         baum.appendChild(el);
         // nächsten Rekursionsschritt starten:
         if (element.childNodes.length>i+1)
            holeKinder(plusminus+"|  ", kind);
         else holeKinder(plusminus+"   ", kind);
      }
      else if (element.childNodes[i].nodeType==3){
         var neuInh = plusminus + "+-- " + kind.nodeValue;
         var inh = pd.createTextNode(neuInh);
         el.appendChild(inh);
         baum.appendChild(el);
      }
   }
}

Auf der CD finden Sie eine Version dieses Skripts, die Sie als Bookmark speichern können. Wenn Sie dieses Bookmark auf einer beliebigen Seite aufrufen, malt es Ihnen den DOM-Baum des Dokuments in ein neues Fenster.

Lösung zu 30:

Eine eigentliche Lösung zu dieser Übung gibt es natürlich nicht. Falls Sie jedoch bei Ihren Versuchen auf nichts Interessantes gestoßen sind, hier ein paar Anregungen:

- Sehen Sie sich die Unterschiede zwischen der DOM-Darstellung von Netscape 6 und Internet Explorer 5 an. Beispielsweise werden Sie entdecken, dass bei IE5 der im title angegebene Titel kein Kind des title-Elements ist. Auch die Behandlung von Zeilenumbrüchen zwischen Tags und in TextNodes ist unterschiedlich: Netscape macht (genau wie IE5 auf MacIntosh) aus jedem Zeilenumbruch zwischen einzelnen Elementen im body einen TextNode.

- Nehmen Sie einmal unter die Lupe, was die Browser aus falschem HTML-Quellcode machen, etwa aus:

<b>au<p></b>weia

Sie werden feststellen: Um die Fehler auszugleichen, werden intern Elemente erzeugt, die im Quelltext gar nicht vorkommen.

- Wenn Sie nicht regelmäßig HTML-Spezifikationen lesen, dürften Sie auch beim Betrachten einer table in der Baum-Ansicht überrascht sein: Laut W3C steht innerhalb des table-Elements stets ein tbody-Element. Wurde es vom Autor der Seite weggelassen, fügen es die Browser von alleine ein.

Linkwerk
screeneXa