Kapitel 19 DHTML IV: Für alle Browser
Aber es ist auch noch nicht zu spät.
Durch Deutschland muss ein Ruck gehen.
– Roman Herzog, Berliner Rede vom 26.4.97
In den drei vorangegangenen Kapiteln wurde derselbe Kardinalfehler immer und immer wieder begangen: Die JavaScript-Programme waren jeweils nur bei etwa einem Drittel aller Browser funktionsfähig. Es ist jedoch nicht allzu schwer, die Beispiele sowohl für den Netscape Navigator als auch für den Internet Explorer (jeweils ab Version 4) funktionsfähig zu machen. In diesem Kapitel werden einige Methoden vorgeführt, mit denen man zwischen den Browsern unterscheiden kann, mit denen man aber auch einige Abkürzungen nutzen kann, damit man denselben Code – nur leicht modifiziert – nicht immer zweimal tippen muss. Eine Methode werden Sie in diesem Kapitel nicht finden – das wäre die Browsererkennung mit dem navigator-Objekt. Zwar funktioniert auch das, aber sie ist schrecklich ineffizient und mitunter nicht zukunftssicher. So unterstützt der Netscape Navigator 6 wie gesehen Layer überhaupt nicht mehr, um nur ein Beispiel zu nennen.
Aber zurück zu der Aufgabe, die Skripte aus den beiden vorhergehenden Kapiteln browserunabhängig zu machen. Die verschiedenen Strategien werden wieder anhand der fünf Beispiele vorgeführt. Wundern Sie sich also nicht, wenn Ihnen Teile des Codes arg bekannt vorkommen, und richten Sie Ihr Augenmerk auf die Details, die Abfragen also, mit denen – je nach Browser – die entsprechenden Kommandos ausgeführt werden.
In Sachen HTML-Code ist die Anpassung einfach. Sowohl der Internet Explorer als auch der Netscape Navigator als auch Netscape 6 unterstützen den <DIV>-Tag, sodass dieses permanent verwendet wird. Der <LAYER>-Tag kommt jetzt nicht mehr vor.
19.1 Animiertes Logo  
Wenn Sie sich den Quellcode für die jeweiligen Browser ansehen, werden Sie feststellen, dass die Unterschiede eigentlich nur bei einer Sorte von Anweisungen auftreten. Jedes Mal nämlich, wenn auf die Position des <DIV>-Tags zugegriffen wird, unterscheiden sich die beiden Browser, und zwar folgendermaßen:
|
Für den Internet Explorer verwenden Sie document.all.logo. style.posLeft bzw. document.all.logo.style.posTop. |
|
Für den Netscape Navigator verwenden Sie document.logo.left bzw. document.logo.top. |
|
Für den Netscape 6 verwenden Sie document.getElementById("logo"). style.left bzw. document.getElementById("logo").style.top. |
Nun ist es leider nicht möglich, die Werte einmal auszulesen und in Variablen zu speichern, da immer der aktuelle Wert benötigt wird. Ein erster Ansatz besteht darin, den Zugriff als Zeichenkette zu speichern und dann den Befehl mittels eval() ausführen zu lassen. Dabei wird folgendermaßen festgestellt, ob der Browser document.all unterstützt, document.getElementById, oder nicht:
if (document.all){
//Code für IE4, IE5.X
} else if (document.layers) {
//Code für NN4.X
} else if (document.getElementById) {
//Code für N6
}
Obwohl keine Layer im Dokument vorkommen, wird dennoch für die Abfrage if (document.layers) verwendet. Das hat den tieferen Sinn, dass bei obiger Abfrage nur dann irgendein Code ausgeführt wird, wenn es sich um einen Browser ab Version 4 handelt; bei älteren Versionen, beispielsweise dem Netscape Navigator 3, passiert nichts, da dort weder document.all noch document.layers noch document.getElementById unterstützt wird.
Der browser-unabhängige Skriptteil kann also folgendermaßen aussehen:
if (document.all)
var x = "document.all.logo.style.posLeft"
else if (document.layers)
var x = "document.logo.left"
else if (document.getElementById)
var x = "document.getElementById('logo').style.left"
function init(){
if (x) //Wenn Variable x gesetzt
eval(x + "= -200")
//Animation starten
animate()
}
function animate(){
if (parseInt(eval(x))>0)
eval(x + "= 0")
if (parseInt(eval(x))<0){
eval(x + " = parseInt(" + x +") + 3")
setTimeout("animate()", 50)
}
}
Hinweis
Beachten Sie, dass für den Netscape 6 zugleich berücksichtigt worden ist, dass die Koordinaten mit parseInt von Strings in Zahlenwerte umgewandelt werden müssen.
Der Netscape Navigator unterstützt beim <DIV>-Tag die relative Positionierung, Sie müssen also nicht die anfängliche Position des Logos auslesen, wie das noch beim Layer der Fall war.
Eine alternative Methode, auf die Position zuzugreifen, besteht darin, eine Funktion zu schreiben, mit der Sie die Werte auslesen und schreiben können. In dieser Funktion dann wird die Fallunterscheidung ausgeführt. Bei mehreren Zuweisungen sparen Sie sich so die Tipparbeit für mehrere Fallunterscheidungen.
function leselinks(){
if (document.all)
return document.all.logo.style.posLeft
else if (document.layers)
return document.logo.left
else if (document.getElementById)
return parseInt(document.getElementById("logo").style.left)
}
function setzelinks(n){
alert("setting "+n)
if (document.all)
document.all.logo.style.posLeft = n
else if (document.layers)
document.logo.left = n
else if (document.getElementById)
document.getElementById("logo").style.left = n+"px"
}
function init(){
if (document.all||document.layers||document.getElementById)
//Wenn Browser ab v4
setzelinks(-200)
//Animation starten
animate()
}
function animate(){
if (leselinks()>0)
setzelinks(0)
if (leselinks()<0){
setzelinks(leselinks() + 3)
setTimeout("animate()", 50)
}
}
Hier noch einmal der gesamte Code, inklusive HTML-Anteil. Ein animiertes Logo, das auf den drei großen Browsern anstandslos funktioniert.
<HTML>
<HEAD>
<TITLE>Animiertes Logo</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function leselinks(){
if (document.all)
return document.all.logo.style.posLeft
else if (document.layers)
return document.logo.left
else if (document.getElementById)
return parseInt(document.getElementById("logo").style.left)
}
function setzelinks(n){
alert("setting "+n)
if (document.all)
document.all.logo.style.posLeft = n
else if (document.layers)
document.logo.left = n
else if (document.getElementById)
document.getElementById("logo").style.left = n+"px"
}
function init(){
if (document.all||document.layers||document.getElementById)
//Wenn Browser ab v4
setzelinks(-200)
//Animation starten
animate()
}
function animate(){
if (leselinks()>0)
setzelinks(0)
if (leselinks()<0){
setzelinks(leselinks() + 3)
setTimeout("animate()", 50)
}
}
//--></SCRIPT>
</HEAD>
<BODY onLoad="init()">
<H3>Animiertes Logo</H3>
<DIV ID="logo" STYLE="position:relative"><IMG SRC="logo.gif"></DIV>
</BODY>
</HTML>
19.2 Drag&Drop  
Beim Drag&Drop-Beispiel müssen wir neben dem Zugriff auf die einzelnen Koordinaten von Maus und <DIV>-Element auch noch das unterschiedliche Event-Handling der beiden Browser betrachten. Fangen wir mit letzterem an: Während der Netscape Navigator die Ereignisse mit captureEvents() abfangen muss, muss man sich beim Internet Explorer nicht so weit verrenken. Um festzustellen, welcher Browser vorliegt, genügt es, beispielsweise das Vorhandensein der Methode capture Events() zu überprüfen:
var logo_x, logo_y
function leselinks(){
if (document.all)
return document.all.logo.style.posLeft
else if (document.layers)
return document.logo.left
}
function setzelinks(n){
if (document.all)
document.all.logo.style.posLeft = n
else if (document.layers)
document.logo.left = n
}
function leseoben(){
if (document.all)
return document.all.logo.style.posTop
else if (document.layers)
return document.logo.top
}
function setzeoben(n){
if (document.all)
document.all.logo.style.posTop = n
else if (document.layers)
document.logo.top = n
}
function init(){
if (document.captureEvents)
document.captureEvents(Event.MOUSEDOWN | Event.MOUSEUP
|Event.MOUSEMOVE)
document.onmousedown = setzestatus
document.onmouseup = setzestatus
document.onmousemove = dragdrop
logo_x = leselinks()
logo_y = leseoben()
}
Wie Sie sehen, wird hier das Lesen und Schreiben der Koordinaten des <DIV>-Containers mit Funktionen erledigt.
Doch zurück zur Ereignisbehandlung. In der Funktion setzestatus() wird einige Male die Art des Ereignisses bestimmt, entweder als e.type oder event.type. Da die Methoden gleich heißen, empfiehlt es sich hier, wie übrigens bereits im Kapitel »Events« beschrieben wurde, eine neue Variable einzuführen, um über diesen Variablenbezeichner auf das jeweilige Ereignis zuzugreifen.
Das zweite Problem besteht in der Abfrage der x- und y-Koordinate der Maus, während das Ereignis eintritt, sowie des Zielelements des Ereignisses (zur Erinnerung: target beim Netscape Navigator, srcElement beim Internet Explorer). Auch hier schreiben wir wieder eine Funktion, die das gewünschte Ergebnis zurückgibt.
Bleibt nur noch eine Frage: Wie wird festgestellt, welches Ereignismodell der verwendete Browser unterstützt? Auch diese Frage ist schnell mit einer Objektüberprüfung zu beantworten. Existiert window.Event, so hat man den Netscape Navigator, ansonsten den Internet Explorer.
Letztendlich muss man nur noch beachten, den Funktionen immer eine Variable e als Parameter zu übergeben. Diese Variable wird zwar nur vom Netscape Navigator benötigt, hat aber auf den Internet Explorer keinen Einfluss, und wir wollen ja Browser-Unabhängigkeit erreichen.
Wie wir bereits erwähnt haben, wurde dieses Beispiel nicht auf Netscape 6 portiert. Sehen Sie aber regelmäßig auf MyGalileo nach, denn dort werden Änderungen und neue Codebeispiele zuerst veröffentlicht!
var bereit = false
var maus_x, maus_y
function x_koord(e){
if (window.Event)
return e.pageX
else
return event.clientX
}
function y_koord(e){
if (window.Event)
return e.pageY
else
return event.clientY
}
function ziel(e){
if (window.Event)
return e.target
else
return event.srcElement
}
function setzestatus(e){
var ev = (window.Event) ? e : window.event
if (ev.type=="mouseup" ||
(ev.type=="mousedown" && (e).name=="logogfx")){
maus_x = x_koord(e)
maus_y = y_koord(e)
bereit = !bereit
if (ev.type=="mousedown"){
logo_x = leselinks()
logo_y = leseoben()
}
}
}
Der letzte Teil, der noch überarbeitet werden muss, ist die Funktion dragdrop(). Dort kommen aber keine neuen Elemente vor, sodass hier gleich der gesamte Quellcode aufgeführt wird. Neuerungen sind wie immer in fetter Schrift dargestellt.
<HTML>
<HEAD>
<TITLE>Drag & Drop</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
var logo_x, logo_y
function leselinks(){
if (document.all)
return document.all.logo.style.posLeft
else if (document.layers)
return document.logo.left
}
function setzelinks(n){
if (document.all)
document.all.logo.style.posLeft = n
else if (document.layers)
document.logo.left = n
}
function leseoben(){
if (document.all)
return document.all.logo.style.posTop
else if (document.layers)
return document.logo.top
}
function setzeoben(n){
if (document.all)
document.all.logo.style.posTop = n
else if (document.layers)
document.logo.top = n
}
function init(){
if (document.captureEvents)
document.captureEvents(Event.MOUSEDOWN | Event.MOUSEUP
| Event.MOUSEMOVE)
document.onmousedown = setzestatus
document.onmouseup = setzestatus
document.onmousemove = dragdrop
logo_x = leselinks()
logo_y = leseoben()
}
var bereit = false
var maus_x, maus_y
function x_koord(e){
if (window.Event)
return e.pageX
else
return event.clientX
}
function y_koord(e){
if (window.Event)
return e.pageY
else
return event.clientY
}
function ziel(e){
if (window.Event)
return e.target
else
return event.srcElement
}
function setzestatus(e){
var ev = (window.Event) ? e : window.event
if (ev.type=="mouseup" ||
(ev.type=="mousedown" && (e).name=="logogfx")){
maus_x = x_koord(e)
maus_y = y_koord(e)
bereit = !bereit
if (ev.type=="mousedown"){
logo_x = leselinks()
logo_y = leseoben()
}
}
}
function dragdrop(){
if (bereit){
var aktuell_x = x_koord(e)
var aktuell_y = y_koord(e)
setzelinks(logo_x + aktuell_x – maus_x)
setzeoben(logo_y + aktuell_y – maus_y)
}
}
//--></SCRIPT>
</HEAD>
<BODY onLoad="init()">
<H3>Drag & Drop</H3>
<DIV ID="logo" STYLE="position:relative"><IMG SRC="logo.gif"></DIV>
</BODY>
</HTML>

19.3 Sichtbar und unsichtbar  
Das dritte Beispiel in den DHTML-Kapiteln ist besonders einfach, und an dieser Stelle soll zusätzlich eine neue Technik eingeführt werden. Die kritischen JavaScript-Befehle sind das Setzen der visibility-Eigenschaft des <DIV>-Containers. Zwar könnte man auch hier mit einer Zeichenkette und eval() arbeiten, aber es ist praktischer, in einer Variablen eine Referenz auf das Objekt zu speichern. Man kann dann auf die visibility-Eigenschaft mittels Objektreferenz.visibility zugreifen und spart sich alle weiteren Abfragen.
Die Variablendefinition geht wie folgt vonstatten:
if (document.all){
var objref1 = document.all.register1.style
var objref2 = document.all.register2.style
var objref3 = document.all.register3.style
} else if (document.layers){
var objref1 = document.register1
var objref2 = document.register2
var objref3 = document.register3
} else if (document.getElementById){
var objref1 = document.getElementById("register1").style
var objref2 = document.getElementById("register2").style
var objref3 = document.getElementById("register3").style
}
Der Rest des Quellcodes ist dann schnell angepasst. Die Abfrage bzw. das Setzen der Koordinaten für die einzelnen <DIV>-Container erfolgt wie gehabt mit einer Funktion. Dabei kann hier schon die Variable objref1, objref2 oder objref3 verwendet werden. Am Beispiel der Funktion leselinks(n) wird das dargestellt. Als Parameter muss noch die Nummer des <DIV>-Containers übergeben werden, da es auf der HTML-Seite ja drei Stück davon gibt.
function leselinks(n){
var obj = eval("objref"+n)
if (document.all)
return obj.posLeft
else if (document.layers||document.getElementById)
return obj.left
}
Es ist noch einiges an Tipparbeit zu leisten, bis man das Skript endlich kompatibel hat. Dann hat man allerdings eine browser-unabhängige Lösung und kann einige der Hilfsfunktionen oder -abfragen immer wieder verwenden.
<HTML>
<HEAD>
<TITLE>Register</TITLE>
<STYLE TEXT="text/css"><!--
A {color:black; text-decoration:none}
--></STYLE>
<SCRIPT LANGUAGE="JavaScript"><!--
var objref1
var objref2
var objref3
function leselinks(n){
eval("var obj = objref"+n)
if (document.all)
return obj.posLeft
else if (document.layers||document.getElementById)
return parseInt(obj.left)
}
function setzelinks(n, wert){
eval("var obj = objref"+n)
if (document.all)
obj.posLeft = wert
else if (document.layers||document.getElementById)
obj.left = wert
}
function leseoben(n){
eval("var obj = objref"+n)
if (document.all)
return obj.posTop
else if (document.layers||document.getElementById)
return obj.top
}
function setzeoben(n, wert){
eval("var obj = objref"+n)
if (document.all)
obj.posTop = wert
else if (document.layers||document.getElementById)
obj.top = wert
}
function init(){
if (document.all){
objref1 = document.all.register1.style
objref2 = document.all.register2.style
objref3 = document.all.register3.style
} else if (document.layers){
objref1 = document.register1
objref2 = document.register2
objref3 = document.register3
} else if (document.getElementById){
objref1 = document.getElementById("register1").style
objref2 = document.getElementById("register2").style
objref3 = document.getElementById("register3").style
}
var layer_x = leselinks(1)
var layer_y = leseoben(1)
setzelinks(2, layer_x)
setzeoben(2, layer_y)
objref2.visibility = "hidden"
setzelinks(3, layer_x)
setzeoben(3, layer_y)
objref3.visibility = "hidden"
}
function register(n){
for (var i=1; i<=3; i++){
var visi = (i==n) ? "visible" : "hidden"
eval("var obj = objref"+i)
obj.visibility = visi
}
}
//--></SCRIPT>
</HEAD>
<BODY onLoad="init()">
<TABLE><TR>
<TD BGCOLOR="red">
<A HREF="javascript:register(1)">Register 1</A>
</TD>
<TD BGCOLOR="green">
<A HREF="javascript:register(2)">Register 2</A>
</TD>
<TD BGCOLOR="blue">
<A HREF="javascript:register(3)">Register 3</A>
</TD>
</TR></TABLE>
<DIV ID="register1" STYLE="position:relative">
<H3>Register 1</H3>
</DIV>
<DIV ID="register2" STYLE="position:relative">
<H3>Register 2</H3>
</DIV>
<DIV ID="register3" STYLE="position:relative">
<H3>Register 3</H3>
</DIV>
</BODY>
</HTML>

19.4 Neuer Mauszeiger  
Das Mauszeigerbeispiel bot schon in den vorherigen Kapitel kaum etwas Neues, sondern hat die bereits bekannten Techniken nur anders angewandt. Grund genug, auch hier nur die neue Version des Skripts abzudrucken. Sie werden übrigens die Funktionen setzelinks() und setzeoben() von oben wieder finden, die nur leicht an das Beispiel angepasst wurden.
<HTML>
<HEAD>
<TITLE>Mauszeiger</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function init(){
if (window.Event)
document.captureEvents(Event.MOUSEMOVE)
document.onmousemove=anim
}
function setzelinks(n){
if (document.all)
document.all.mauszeiger.style.posLeft = n
else if (document.layers)
document.mauszeiger.left = n
else if (document.getElementById)
document.getElementById("mauszeiger").style.left = n
}
function setzeoben(n){
if (document.all)
document.all.mauszeiger.style.posTop = n
else if (document.layers)
document.mauszeiger.top = n
else if (document.getElementById)
document.getElementById("mauszeiger").style.top = n
}
function anim(e){
var x = (window.Event) ? e.pageX : event.clientX
var y = (window.Event) ? e.pageY : event.clientY
setzelinks(x)
setzeoben(y)
}
//--></SCRIPT>
</HEAD>
<BODY BGCOLOR="white" onLoad="init()">
<H1>Mauszeiger</H1>
<DIV ID="mauszeiger" STYLE="position:absolute">
<IMG SRC="logo.gif">
</DIV>
</BODY>
</HTML>
19.5 Permanentes Werbebanner  
Das letzte Beispiel war das immer sichtbare Werbebanner, das natürlich auch als Sitemap oder ähnliches zweckentfremdet werden kann. Da in diesem Beispiel keine Ereignisse eingesetzt werden, spart man sich schon einmal den halben Aufwand bei der Cross-Browser-Programmierung. Lediglich auf die einzelnen Objekteigenschaften muss in Abhängigkeit vom verwendeten Browser zugegriffen werden. Auch hier verwenden wir wieder setzelinks() und setzeoben(), und arbeiten sonst mit den schon gewohnten Fallunterscheidungen: if(document.layers), if(document.all) und if(document.getElementById).
<HTML>
<HEAD>
<TITLE>Werbebanner</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function init(){
if (document.layers)
document.logo.visibility="visible"
else if (document.all)
document.all.logo.style.visibility="visible"
else if (document.getElementById)
document.getElementById("logo").style.visibility="visible"
anim()
}
function setzelinks(n){
if (document.all)
document.all.logo.style.posLeft = n
else if (document.layers)
document.logo.left = n
else if (document.getElementById)
document.getElementById("logo").style.left = n
}
function setzeoben(n){
if (document.all)
document.all.logo.style.posTop = n
else if (document.layers)
document.logo.top = n
else if (document.getElementById)
document.getElementById("logo").style.top = n
}
function anim(){
if (document.layers){
var x=window.innerWidth+window.pageXOffset-100
var y=window.pageYOffset
} else if (document.all){
var x=document.body.clientWidth+document.body.scrollLeft-89
var y=document.body.scrollTop
} else if (document.getElementById){
var x=window.innerWidth+window.pageXOffset-100
var y=window.pageYOffset
}
setzelinks(x)
setzeoben(y)
setTimeout("anim()", 500)
}
//--></SCRIPT>
</HEAD>
<BODY BGCOLOR="white" onLoad="init()">
<H1>Werbebanner</H1>
<DIV ID="logo" STYLE="visibility:hide;position:absolute">
<A HREF="http://www.galileo-press.de"><IMG SRC="logo.gif" BORDER="0"></A>
</DIV>
<SCRIPT LANGUAGE="JavaScript"><!--
for (var i=0; i<30; i++)
document.write("Fülltext")
for (i=0; i<3; i++){
for (var j=0; j<10; j++)
document.write("<"+"BR"+">")
document.write("Fülltext")
}
//--></SCRIPT>
</BODY>
</HTML>
Zum Ende dieses Kapitels noch ein kurzer Ausblick: Die Möglichkeiten von DHTML sind fast unendlich. Für weitere Informationen lohnt es sich – wie bereits erwähnt – den Katalog von Galileo Press zu studieren. An dieser Stelle kann leider kein ausführlicher Einstieg in die Materie gegeben werden, da dies Umfang und Zielsetzung dieses Buches sprengen würde. Aus demselben Grund können hier auch nicht alle Inkompatibilitäten der verschiedenen Browser aufgelistet werden. Das Ziel der letzten vier Kapitel war es, einen browser-unabhängigen Einstieg in die Materie zu geben. Mit den vorgestellten Techniken, der Referenz und ein wenig Fantasie und Freude am Ausprobieren können Sie beeindruckende Ergebnisse erzielen.
19.6 Fragen & Aufgaben  
1. |
Gehen Sie zu dem Beispiel mit den zwei verschachtelten Layern zurück. Wie können Sie von der Schaltfläche aus auf das Hauptdokument zugreifen? |
2. |
Ändern Sie das Beispiel mit dem animierten Logo so, dass a) das Logo von rechts einschwebt, b) es diagonal von links oben kommt. |
3. |
Ändern Sie das permanente Werbebanner so um, dass es auf Mausklick verschwindet. |
|