(
KOMPENDIUM
)
AJAX
Kompendium Kompetent aufbereitetes PC-Know-how für alle Die KOMPENDIEN aus dem Markt+Technik Verlag stehen seit mehr als 20 Jahren für anerkanntes Expertenwissen und bieten wertvolle Praxistipps in allen Fragen rund um den PC. Das Portfolio der Handbücher reicht von der übersichtlichen Vorstellung diverser Programmiersprachen bis hin zur umfangreichen Beschreibung kompletter Betriebssysteme: Mit mehr als 500 Titeln seit Bestehen der Reihe wurde nahezu jede Fragestellung der Computerpraxis abgedeckt. Ob als Lehrbuch für den ambitionierten Einsteiger oder Nachschlagewerk für den erfahrenen Anwender: Die übersichtlichen, klar strukturierten KOMPENDIEN helfen jedem schnell weiter und auch komplexe Sachverhalte werden mit praxisnahen Beispielen übersichtlich illustriert und verständlich gemacht. Ein detailliertes Inhaltsverzeichnis und ein umfangreicher Index ermöglichen dem Leser außerdem schnellen Zugriff auf die gesuchten Informationen. Technisch anspruchsvoll und präzise, dabei jedoch immer praxisbezogen und klar verständlich: Das sind die KOMPENDIEN, die mit mehr als 6 Millionen Lesern zu den erfolgreichsten Computerfachbüchern auf dem deutschsprachigen Markt gehören.
AJAX Web 2.0-Anwendungen mit JavaScript und XML T O B I AS HAU SE R
(
KOMPENDIUM Einführung | Arbeitsbuch | Nachschlagewerk
)
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar. Die Informationen in diesem Buch werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen und weitere Stichworte und sonstige Angaben, die in diesem Buch verwendet werden, sind als eingetragene Marken geschützt. Da es nicht möglich ist, in allen Fällen zeitnah zu ermitteln, ob ein Markenschutz besteht, wird das Symbol ® in diesem Buch nicht verwendet. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt.
10 9 8 7 6 5 4 3 2 1 10 09 08 07 ISBN 978-3-8272-4175-7
© 2007 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Coverkonzept: independent Medien-Design, Widenmayerstraße 16, 80538 München Covergestaltung: Thomas Arlt,
[email protected] Titelfoto: mauritius images, USA, Texas, Dallas, Skyline Fachlektorat: Mathias Hertel, Pfinztal Lektorat: Boris Karnikowski,
[email protected] Korrektorat: Marita Böhm, München Herstellung: Elisabeth Prümm,
[email protected] Satz: reemers publishing services gmbh, Krefeld Druck und Verarbeitung: Bercker, Kevelaer Printed in Germany
Überblick
Überblick
Vorwort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
Teil 1
JavaScript
27
Kapitel 1
Wissenswertes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
Kapitel 2
Syntax und Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
Kapitel 3
Programmieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
Kapitel 4
Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
119
Kapitel 5
OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
145
Kapitel 6
Objekte in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
179
Kapitel 7
Arrays und Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
219
Kapitel 8
Document Object Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
265
Kapitel 9
Ereignisse und Event-Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
297
Teil 2
Ajax
315
Kapitel 10 Architektur und Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
317
Kapitel 11
XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
343
Kapitel 12 JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
363
Kapitel 13 Problemlösungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
371
Teil 3
383
Webanwendungen
Kapitel 14 Browserunterscheidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
385
Kapitel 15 Bilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
417
Kapitel 16 Navigationshilfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
451
Kapitel 17
Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
477
Kapitel 18 Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
511
Kapitel 19 Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
527
5
Überblick
Kapitel 20 Formulare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
553
Kapitel 21 Vollständigkeitsüberprüfung und reguläre Ausdrücke . . . . . . . . . . . . .
597
Kapitel 22 CSS und JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
621
Kapitel 23 Dynamisches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
639
Kapitel 24 Multimedia, Java etc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
683
Teil 4
703
Ajax-Frameworks
Kapitel 25 Xajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
705
Kapitel 26 Prototype und scriptaculous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
725
Kapitel 27 Microsoft ASP.NET AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
767
Kapitel 28 Mehr Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
799
Teil 5
809
Arbeitshilfen
Kapitel 29 Debugging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
811
Kapitel 30 Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
841
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
865
6
Inhalt
Inhalt
Vorwort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
Warum dieses Buch? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
Aufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
Hilfe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
Team . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
Teil 1
JavaScript
27
Kapitel 1
Wissenswertes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
1.1 1.1.1 1.1.2
Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Browserkrieg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Heute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30 31
1.2
ECMAScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
1.3
Weitere Einsatzgebiete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
1.4
JavaScript und Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
1.5 1.5.1 1.5.2 1.5.3 1.5.4
JavaScript-Einbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alte Browser und deaktiviertes JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . Position des Skript-Bereichs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Externes JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MIME-Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34 36
1.6
JavaScript in Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
1.7
JavaScript bei Ereignissen (Event-Handler). . . . . . . . . . . . . . . . . . . . . . . . . .
44
1.8 1.8.1 1.8.2
Versionsunterscheidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JavaScript-Versionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Browserunterscheidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46 46
32
38 40 42
48
7
Inhalt
Kapitel 2
Syntax und Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8
Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gleitkommazahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spezielle Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51 52
2.2 2.2.1 2.2.2 2.2.3 2.2.4
Variablen deklarieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Werte zuweisen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Werte ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Strichpunkt (;) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrere Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60 61
2.3 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7
Bezeichner und Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Groß- und Kleinschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablennamen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reservierte Schlüsselwörter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichensatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typkonvertierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64 64
2.4 2.4.1 2.4.2
Hilfsmittel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70 70
Kapitel 3
Programmieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
3.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6
Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arithmetische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String-Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergleichsoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Logische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bitweise Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operator-Präferenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75 75
3.2 3.2.1 3.2.2
Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . if-Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . switch case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
95 95
8
54 55 58 59 59 59 59
62 63 63
64 65 67 68 69 70
72
79 80 86 89 93
101
Inhalt 3.3 3.3.1 3.3.2 3.3.3
Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . do while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
106 106
Kapitel 4
Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
119
4.1 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8
Eigene Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rückgabewert (return) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen abbrechen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsaufrufe verschachteln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen verschachteln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lokale und globale Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rekursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
119 121
4.2
Funktionen als Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
132
4.3 4.3.1 4.3.2 4.3.3 4.3.4
Globale Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zahlentests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datentypen ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . eval() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
133 133
Kapitel 5
OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
145
5.1 5.1.1 5.1.2 5.1.3 5.1.4
Was sind Objekte? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Polymorphismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
146 147
5.2 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5
Eigene Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sinn und Unsinn von eigenen Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
150 151
5.3
for-in für Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
158
5.4
with . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
159
5.5
prototype-Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
160
115 117
122 124 125 126 127 129 130
135 138 140
148 149 150
152 154 156 157
9
Inhalt
5.6 5.6.1 5.6.2
Das Objekt Object und die Vererbungskette . . . . . . . . . . . . . . . . . . . . . . . . . Objekte mit Object definieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften und Methoden von Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
162 164
5.7 5.7.1
Vorhandene Objekte erweitern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Math erweitern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
168 170
5.8 5.8.1 5.8.2 5.8.3 5.8.4
Funktionen und Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen als Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften von Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden von Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Objekte an Funktionen übergeben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
172 172
Kapitel 6 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7
Objekte in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Proprietäre Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
179 180
Kapitel 7
Arrays und Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
219
7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.1.6 7.1.7 7.1.8 7.1.9
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alternativen und Kurzformen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elemente ändern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Länge eines Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . for-in- und andere Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Array-Elemente löschen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Assoziative Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden für Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alternative Datenspeicherung mit eval() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Neuerungen in JavaScript 1.6 und 1.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
219 220
7.2 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.2.6 7.2.7
Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String-Länge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Groß- und Kleinbuchstaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden zum Arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Suchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . toString() und valueOf(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Neuerungen in JavaScript 1.6 und 1.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
248 248
10
164
173 175 177
190 213 214 216 216 216
223 223 224 226 227 229 245 247
249 251 253 258 262 262
Inhalt Kapitel 8
Document Object Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
265
8.1 8.1.1 8.1.2 8.1.3 8.1.4
W3C-DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften und Methoden des W3C-DOM. . . . . . . . . . . . . . . . . . . . . . . . . . . W3C-DOM und DHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
267 268
8.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.2.7 8.2.8
DOM in verschiedenen Browsern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DOM Level 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DOM Level 0 mit Bild-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netscape 4.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . W3C-DOM Level 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . W3C-DOM Level 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . W3C-DOM Level 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
286 287
8.3 8.3.1 8.3.2 8.3.3 8.3.4 8.3.5 8.3.6
Browserobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . navigator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . history . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
290 290
Kapitel 9
Ereignisse und Event-Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
297
9.1 9.1.1 9.1.2 9.1.3
Ereignis-Modelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netscape Navigator- (und Mozilla-)Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer-Modell. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . W3C-Ereignis-Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
297 298
9.2 9.2.1 9.2.2 9.2.3
Ereignisse anwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Standardmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Browserübergreifend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Besonderheiten des Internet Explorers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
307 308
9.3
Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
310
9.4
Tastatur-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
312
269 279 285
287 287 288 288 289 289 289
292 292 292 293 294
301 304
309 310
11
Inhalt
Teil 2
Ajax
315
Kapitel 10
Architektur und Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
317
10.1
Ein erstes Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
317
10.2
Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
321
10.3
Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
323
10.4 10.4.1 10.4.2
XMLHttpRequest browserübergreifend . . . . . . . . . . . . . . . . . . . . . . . . . . . . XMLHttpRequest als globale Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XMLHttpRequest kapseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
323 326 327
10.5
Asynchron und synchron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
330
10.6 10.6.1 10.6.2 10.6.3
Steuerung und Rückgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Antworttypen und Austauschformate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
333 334
10.7
Werte an den Server übermitteln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
338
Kapitel 11
XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
343
11.1
XML-Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
343
11.2
XML und Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
345
11.3 11.3.1 11.3.2 11.3.3 11.3.4
XML in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML laden und erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML-DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XSLT mit JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XPath mit JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
346 347
11.4
E4X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
360
Kapitel 12
JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
363
12.1
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
364
12.2
Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
366
12.3
JSON und Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
367
Kapitel 13
Problemlösungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
371
13.1
Status und History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
371
13.2
Sicherheitsbeschränkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
375
13.3
Accessibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
376
12
336 336
349 355 358
Inhalt 13.4 13.4.1 13.4.2 13.4.3 13.4.4
Timing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netzwerklatenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Serverprobleme. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inaktivität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konkurrierende Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
376 377
13.5
Cacheprobleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
381
Teil 3
Webanwendungen
383
Kapitel 14
Browserunterscheidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
385
14.1 14.1.1
language-Attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sinnvoll? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
386 386
14.2
Deaktiviertes JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
387
14.3 14.3.1 14.3.2 14.3.3 14.3.4 14.3.5
navigator-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Welcher Browser? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Browser-Sniffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sprache feststellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sinnvoll? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
388 388
14.4 14.4.1 14.4.2
Browserobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ajax/DHTML und Browserunterscheidung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sinnvoll? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
408 409
14.5
Serverseitige Browserunterscheidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
413
Kapitel 15
Bilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
417
15.1 15.1.1 15.1.2 15.1.3 15.1.4 15.1.5 15.1.6
Rollover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rollover-Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Automatisiert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Automatisiert mit this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ältere Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zufall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipps und Tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
417 418
15.2 15.2.1 15.2.2 15.2.3
Vorladen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrere Bilder vorladen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alles geladen? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehler beim Laden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
424 425
15.3
Bilder als Formularelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
428
379 380 380
401 402 407 408
411
419 420 421 422 423
426 427
13
Inhalt
15.4 15.4.1 15.4.2 15.4.3 15.4.4
Animation? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dateiformate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bildertausch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Größenänderung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipps und Tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
432 433
15.5 15.5.1
Formatierung und Verschönerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alternativtext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
445 447
15.6
Imagemaps und JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
448
Kapitel 16
Navigationshilfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
451
16.1 16.1.1
history-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mozilla und Netscape Navigator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
451 453
16.2 16.2.1 16.2.2 16.2.3
Weiterleitung und das location-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeitversetzt umleiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anker usw. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Neu laden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
453 454
16.3 16.3.1 16.3.2
Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Linkziel ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
457 458
16.4 16.4.1 16.4.2 16.4.3
Statusleiste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Automatisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laufschrift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipps & Tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
461 462
16.5 16.5.1
Laufschrift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Laufschrift schützen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
471 474
16.6
Drucken mit JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
475
Kapitel 17
Fenster. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
477
17.1 17.1.1 17.1.2 17.1.3 17.1.4 17.1.5
Dialogfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Warnmeldung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bestätigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eingabefenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dialogfelder im Internet Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modale Fenster im Web 2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
477 477
17.2 17.2.1 17.2.2 17.2.3
Popups und andere neue Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Popup-Blocker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fokus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
488 490
14
433 442 445
455 456
460
464 469
478 479 481 484
492 497
Inhalt 17.2.4 17.2.5 17.2.6
In Popups schreiben. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fenster schließen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . opener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
500
17.3 17.3.1
Scrollen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Scrollen verhindern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
504 506
17.4
Fenster bewegen und skalieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
506
Kapitel 18
Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
511
18.1 18.1.1 18.1.2 18.1.3
Geschichte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Beschränkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cookies im Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
512 512
18.2 18.2.1 18.2.2 18.2.3 18.2.4
Mit Cookies arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Setzen und auslesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Löschen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrere Informationen in einem Cookie . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
516 516
18.3
Cookietest. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
523
18.4
Serverseitig, clientseitig oder Ajax? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
524
Kapitel 19
Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
527
19.1 19.1.1 19.1.2 19.1.3
Struktur und Zugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignisse und Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Frames formatieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verschachtelte Framesets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
528 530
19.2 19.2.1 19.2.2 19.2.3 19.2.4
Häufige Anwendungen und Tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Frames und Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unsichtbare Frames – eine Ajax-Alternative? . . . . . . . . . . . . . . . . . . . . . . . . . . Frames füllen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . History und Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
542 542
19.3
Frames erzwingen und vermeiden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
547
19.4 19.4.1 19.4.2 19.4.3
iFrames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JavaScript-Zugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iFrame-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Praxiseinsatz und Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
547 548
498 502
513 514
522 522 523
532 538
544 545 546
550 551
15
Inhalt
Kapitel 20
Formulare. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
553
20.1 20.1.1 20.1.2
Zugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff mit name-Attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff mit forms-Kollektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
553 554
20.2 20.2.1 20.2.2 20.2.3 20.2.4 20.2.5 20.2.6 20.2.7 20.2.8 20.2.9
Formularelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Textfeld . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrzeilige Textfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Checkbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Radiobutton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auswahlliste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datei-Upload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schaltflächen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . fieldset, legend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . label . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
556 556
20.3 20.3.1 20.3.2 20.3.3
Formular versenden … . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . … an serverseitiges Skript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... per E-Mail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . … per Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
591 591
Kapitel 21
Vollständigkeitsüberprüfung und reguläre Ausdrücke . . . . . . . . . . . . . . . . . .
597
21.1 21.1.1 21.1.2 21.1.3 21.1.4 21.1.5 21.1.6
Vollständigkeitsüberprüfung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formularelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Automatisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nachfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überprüfung bei der Eingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auf dem Server? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
597 598
21.2
Formulardaten übernehmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
608
21.3 21.3.1 21.3.2
Reguläre Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mit regulären Ausdrücken arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
611 613
21.4
Nutzer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
618
Kapitel 22
CSS und JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
621
22.1 22.1.1 22.1.2 22.1.3
CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einheiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Farben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
622 622
16
555
562 563 565 567 581 582 589 590
592 593
601 604 606 606 607
615
623 623
Inhalt 22.2 22.2.1 22.2.2
Zugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das style-Objekt und seine Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netscape Navigator 4.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
625 625
22.3 22.3.1 22.3.2
Klassen und Stylesheets wechseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen wechseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Stylesheets wechseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
628 628
22.4 22.4.1 22.4.2
styleSheets-Kollektion und Regeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Regeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Weitere Eigenschaften und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
633 633
22.5
Stylesheets und Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
637
Kapitel 23
Dynamisches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
639
23.1 23.1.1 23.1.2 23.1.3 23.1.4 23.1.5
Historie und Grundlagen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netscape 4.x und Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer-Spezialitäten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Neuere Ansätze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kleine Fallen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
640 641
23.2 23.2.1 23.2.2 23.2.3 23.2.4
Maus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mauszeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auf den Spuren der Maus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drag&Drop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehr Drag&Drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
651 652
23.3 23.3.1 23.3.2 23.3.3
Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sichtbare und unsichtbare Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . zIndex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elemente permanent sichtbar machen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
664 665
23.4 23.4.1 23.4.2 23.4.3 23.4.4 23.4.5 23.4.6
Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorbereitungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dynamische Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsaufruf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Komplett . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erweiterungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
674 675
628
629
636
641 641 645 651
653 657 664
668 670
676 677 677 678 680
17
Inhalt
Kapitel 24
Multimedia, Java etc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
683
24.1 24.1.1 24.1.2 24.1.3
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Liveconnect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf Java-Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Java zu JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
683 684
24.2 24.2.1 24.2.2 24.2.3 24.2.4 24.2.5 24.2.6
Plugins. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In die Website einbinden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Plugin-Erkennung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Steuerungsmöglichkeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Apple QuickTime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RealPlayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
689 689
24.3 24.3.1 24.3.2 24.3.3
Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JavaScript zu Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Flash zu JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommunikationssorgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
696 697
Teil 4
Ajax-Frameworks
703
Kapitel 25
Xajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
705
25.1
Einrichtung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
706
25.2
Ajax-Verbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
706
25.3 25.3.1 25.3.2 25.3.3 25.3.4 25.3.5
Nützliche Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konfiguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter übernehmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Request steuern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JavaScript-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
708 708
25.4
Produktsuche – ein Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
719
25.5
Besonderheiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
723
Kapitel 26
Prototype und scriptaculous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
725
26.1
Einrichtung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
726
26.2 26.2.1 26.2.2
Ajax-Verbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Event-Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
729 732
18
685 687
691 694 694 695 696
699 702
710 712 716 718
734
Inhalt 26.2.3 26.2.4
Ajax.Responders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ajax.Updater und Ajax.PeriodicalUpdater . . . . . . . . . . . . . . . . . . . . . . . . . . . .
738
26.3 26.3.1 26.3.2 26.3.3 26.3.4
Nützliches in Prototype. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Hilfsfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JSON, Strings und Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DOM-Einsatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Prototype erweitern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
744 744
26.4 26.4.1 26.4.2 26.4.3
Nützliches in scriptaculous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Effekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drag&Drop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ajax-Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
752 752
Kapitel 27
Microsoft ASP.NET AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
767
27.1
Einrichtung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
768
27.2 27.2.1 27.2.2 27.2.3 27.2.4 27.2.5 27.2.6
ASP.NET 2.0 AJAX Extensions 1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfacher Ajax-Aufruf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Trigger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fortschrittsanzeige – UpdateProgress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clientseitige Steuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Regelmäßige Aktualisierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
770 771
27.3
ASP.NET AJAX Control Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
785
27.4 27.4.1 27.4.2 27.4.3
ASP.NET (AJAX) Futures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML-Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drag&Drop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
790 791
Kapitel 28
Mehr Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
799
28.1
Rico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
799
28.2
Dojo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
799
28.3
Sajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
800
28.4
MochiKit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
801
28.5
Direct Web Remoting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
802
28.6
Ajax.NET Professional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
803
28.7
Spry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
806
740
745 748 750
756 762
774 774 775 777 779
793 794
19
Inhalt
28.8
Google Web Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
806
28.9
Yahoo! UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
807
Teil 5
Arbeitshilfen
809
Kapitel 29
Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
811
29.1 29.1.1 29.1.2 29.1.3 29.1.4
Debugging mit und im Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internet Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Netscape Navigator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mozilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übrige Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
811 812
29.2
Editoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
821
29.3 29.3.1 29.3.2 29.3.3 29.3.4 29.3.5
HTTP-Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Firebug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tamper Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . LiveHTTPHeaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ieHTTPHeaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fiddler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
822 822
825
29.4 29.4.1
Fehler und Lösungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Endlosschleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
825 827
29.5 29.5.1 29.5.2 29.5.3 29.5.4
Fehler-Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehler abfangen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehlerereignis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wann ist Fehler-Handling sinnvoll? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
828 828
Kapitel 30
Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
841
30.1 30.1.1 30.1.2 30.1.3 30.1.4 30.1.5
Sicherheitslücken in der Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . Cross Site Scripting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JavaScript-Code-Inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML-Poisoning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SQL-Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ajax-Risiken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
841 843
30.2
Datenklau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
848
30.3 30.3.1 30.3.2
Skripte absichern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rechte Maustaste unterbinden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code in Frames auslagern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
849 849
20
814 816 820
823 824 824
836 837 839
843 847 847 848
851
Inhalt 30.3.3 30.3.4 30.3.5 30.3.6
Externe Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quellcode schwer lesbar machen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quellcode verschlüsseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sichern oder nicht? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
852
30.4
Passwortschutz mit JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
855
30.5 30.5.1 30.5.2
Sicherheitslecks in Browsern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lecks? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zertifikate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
855 855 856
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
865
853 853 854
21
Vorwort
»Ajax isn’t a technology.« – Jesse James Garett1 Dieses Zitat stammt aus dem Artikel, mit dem Jesse James Garett das Kunstwort Ajax geprägt hat. Er meint damit, dass Ajax verschiedene Technologien vermischt. Da sind XML und das Document Object Model zu nennen, aber natürlich auch HTML und CSS zur Präsentation der Daten. Das Bindeglied zwischen diesen Elementen ist JavaScript. Dieses Buch zeigt deswegen nicht nur, wie Sie Ajax nutzen, um moderne Web 2.0-Anwendungen zu entwickeln, sondern Sie erfahren auch, wie JavaScript funktioniert und wie Sie damit XML und das Document Object Model nutzen.
Warum dieses Buch? Ajax-Bücher gibt es einige. Allerdings wenden sich viele an erfahrene JavaScript-Entwickler oder beginnen bei Adam und Eva, sprich bei HTML und CSS. Dieses Buch beginnt bei dem, was Sie für Ajax-Anwendungen auf jeden Fall benötigen: bei JavaScript. Sie können ohne Programmiervorkenntnisse starten, nur HTML- und CSS-Kenntnisse sollten Sie mitbringen. Sie lernen in diesem Buch nicht nur, wie Sie per Ajax Daten mit dem Server austauschen und XML nutzen, sondern Sie lernen auch, wie Sie dank JavaScript mit Bildern und Formularen arbeiten, wie Sie die JavaScript-Objekte nutzen und wie Sie Drag&Drop und Animationen realisieren. Außerdem erhalten Sie einen umfassenden Einblick in die Welt der Ajax-Frameworks und haben so die Möglichkeit, sich aus verschiedenen Konzepten die für Sie geeignete Lösung auszusuchen. Und zu guter Letzt sorgen über 500 Beispiele im Download-Archiv dafür, dass Sie sofort loslegen können. Zugegeben, dicke Bücher sind schwer zu tragen, aber wenn Sie sich dieses Buch ins Regal stellen, haben Sie auf jeden Fall auch noch lange nach dem Einstieg in Ajax und JavaScript ein gutes Nachschlagewerk. Und der Weg vom Schreibtisch zum Regal ist ja normalerweise überschaubar.
Übersetzung: »Ajax ist keine Technologie«, Jesse James Garret 2005 in: »Ajax: A New Approach to Web Applications« (http://www.adaptivepath.com/publications/essays/archives/000385.php)
23
ndex
1
Vorwort
Aufbau Das Buch besteht aus fünf Teilen. Ein Überblick zeigt Ihnen, was Sie erwartet: ■
■
■
■
■
Teil 1 enthält die Grundlagen von JavaScript und behandelt alle nötigen Bausteine für fortgeschrittenes Programmieren. Die Kapitel von Teil 1 hängen inhaltlich zusammen und bauen aufeinander auf. Sollte der geneigte Leser allerdings einzelne Teile daraus später benötigen, findet er sich auch beim direkten Nachschlagen gut zurecht. Teil 2 zeigt die grundlegende Ajax-Architektur, das XMLHttpRequestObjekt und führt in die Nutzung von XML und JSON ein. Ein eigenes Kapitel ist den Problemen rund um Ajax-Anwendungen gewidmet. Teil 3 enthält eine Vielzahl nützlicher Webanwendungen, die aus dem Ajax-Aufruf eine richtige Applikation machen. Hier werden Fenster aufgerufen, mit Formularen interagiert, auf Vollständigkeit geprüft und Elemente animiert. Teil 4 kümmert sich um Ajax-Frameworks, die dem Entwickler eine Menge Arbeit abnehmen und hervorragende Funktionen von der Aufrufsteuerung bis hin zum Drag&Drop bieten. Der Teil stellt drei AjaxFrameworks ausführlicher vor, die sehr unterschiedliche Konzepte illustrieren. Anschließend folgt noch ein Kapitel, das andere wichtige Frameworks auflistet. Teil 5 zeigt, wie Sie möglichst fehlerfreie Anwendungen schreiben und Fehlern schnell auf die Spur kommen. Außerdem ist dem wichtigen Thema Sicherheit ein eigenes Kapitel gewidmet.
Browser Eine der Kernfragen für Ajax- und JavaScript-Entwickler ist nach wie vor, welche Funktionen in welchen Browsern zur Verfügung stehen. Dieses Buch beschreitet hier einen eigenen Weg: Sie finden am Ende keine dicke Referenz, sondern stattdessen bei allen wichtigen Elementen eine Tabelle über die Browserkompatibilität: Tabelle alert() alert()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Darin sehen Sie auf einen Blick, für welchen Browser die jeweilige Funktion vorhanden ist. Dabei sind alle modernen Browser von Mozilla/Firefox und Netscape Navigator in neueren, auf Mozilla basierenden Versionen (M/FF) über Internet Explorer 6 und 7 (IE6 und IE7) bis Opera (Op) und Safari/Konqueror (SF/KQ) vertreten. Außerdem werden die älteren Browser Netscape Navigator 4 (NS4) und Internet Explorer 4 bis 5.5 (IE4, IE5 und IE5.5) noch
24
Service
berücksichtigt, wenn Sie beispielsweise abwärtskompatible Anwendungen für ein Intranet schreiben müssen oder eine möglichst große Zielgruppe erreichen möchten.
Service 1
Auf dem Weg zum Ajax- und JavaScript-Profi werden Sie beim Lesen einige Icons unterstützen:
2 Dieses Symbol deutet an, dass Sie hier zusätzliche Informationen finden. INFO
3
Einen Verweis auf eine andere Stelle, die für ein Thema ebenfalls von Interesse ist oder einen Themenbereich vertieft, finden Sie bei diesem Symbol.
4
REF
Achtung, hier versteckt sich eine Falle oder eine Information, die Ihre besondere Aufmerksamkeit erfordert!
5 HALT
6
Egal ob schnellere Lösungen oder zusätzliche Möglichkeiten: Informationen neben diesem Symbol geben nützliche Tipps. TIPP
7 Neben diesem Symbol finden sie Hintergrundinformationen, die nicht im engeren Sinne mit dem Thema des Kapitels zu tun haben, aber dennoch wissenswert sind.
EXKURS
8
Alle Beispiele zum Buch erhalten Sie als Download unter http://www. hauser-wenz.de/goto/ajaxkompendium (Downloadgröße ca. 1,3 MByte). Im Zip-Archiv finden Sie die Beispiele nach Kapiteln sortiert.
9 10
Listing Ein Beispiel (beispiel.html)
Im abgedruckten Code sind wichtige Stellen fett hervorgehoben, um Ihnen die Übersicht zu erleichtern. Dieses Icon deutet an, dass im Download-Archiv eine passende Datei liegt. Meist sind dies Dateien, die vorhandene Listings abwandeln und weitere Alternativen aufzeigen oder die für Beispiele im Buch benötigt werden. Insgesamt finden Sie über 500 Listings im Archiv.
11
12 WWW
13 14
25
Vorwort
Hilfe Der Kerngedanke dieses Buches ist es, dem (angehenden) Ajax- und JavaScript-Entwickler das bestmögliche Hilfsmittel mitzugeben. Sollten Sie als Entwickler und Leser dieses Buches ■ ■ ■ ■
etwas vermissen, einen Fehler finden, eine Anregung loswerden wollen oder einfach nur eine Meinung zum Buch haben,
melden Sie sich über den Kontakt unter http://www.hauser-wenz.de/ support/kontakt/. Neuigkeiten und eine Errataliste zum Buch finden Sie auf der Website zum Buch http://www.hauser-wenz.de/support/ unter der ISBN des Buches.
TIPP
Leider kann ich nur Fragen beantworten, die sich direkt um den Inhalt dieses Buches drehen. Dafür versuche ich aber, die Antwortzeiten so kurz wie möglich zu halten.
Team Dass dieses Buch heute in Ihren Händen liegt, verdanken Sie als Leser und noch viel mehr ich als Autor vielen Menschen, die den Entstehungsprozess ganz oder teilweise begleitet haben: ■ ■ ■ ■ ■ ■
meinem Lektor Boris Karnikowski für sein Engagement und den Glauben an das Projekt, meinem Fachlektor Matthias Hertel, der mit seinen Kenntnissen für viele wertvolle Anregungen verantwortlich war, meiner Korrektorin Marita Böhm, die auch bei großer Zeitknappheit den Duden nicht aus den Augen verloren hat, Christian und Andi für die Unterstützung, Stefanie für nächtliche Arbeitssitzungen, Marianne.
Vielen Dank! Und zum Schluss noch ein Dank an Sie, lieber Leser! Denn für Sie ist dieses Buch geschrieben. Tobias Hauser
26
Teil 1 JavaScript Der erste Teil dieses Buches zeigt Ihnen die wichtigsten Grundlagen für jede Ajax- und JavaScript-Anwendung: JavaScript pur. Sie lernen die Grundlagen der Sprache kennen und erhalten einen tiefen Einblick in die wichtigsten Konzepte, Elemente und Objekte. Auch wenn Sie schon Ajax-Lösungen und fortgeschrittene Anwendungen programmieren, werden Sie in diesem Teil viele interessante Informationen finden.
29
Wissenswertes
1
51
Syntax und Variablen
2
75
Programmieren
3
119
Funktionen
4
145
OOP
5
179
Objekte in JavaScript
6
219
Arrays und Strings
7
265
Document Object Model
8
297
Ereignisse und Event-Handler
9
Inhalt
1
Wissenswertes 1 2
Web 2.0 und Ajax haben sich in kurzer Zeit zu einem Hype entwickelt. Die Basis aller Ajax-Anwendungen ist allerdings JavaScript als Skriptsprache im Browser. Im World Wide Web war JavaScript schon immer ein wichtiger (aber wenig beachteter) Bestandteil. Wenn Sie einen Blick in den Quellcode irgendeiner größeren Website werfen, sehen Sie dort sicherlich einen -Block mit JavaScript-Code. Dieser Code wird im Browser interpretiert (ausgeführt) und ist clientseitig, da das Ausführen des Codes auf dem Rechner des Nutzers (Client) geschieht.
3
4
Abbildung 1.1: Die (vereinfachte) Client-ServerArchitektur
5
6
7
8
9
10
Nimmt man nun noch Ajax hinzu, wird die Architektur ein klein wenig komplizierter, hat aber grundlegend noch dieselbe Basis. Anfrage und Antwort erfolgen über HTTP. Die Ajax-Anfrage und Ajax-Antwort laufen allerdings über ein spezielles Browserobjekt in JavaScript: XMLHttpRequest. Und das Wichtigste: Diese Anfragen werden durchgeführt, ohne dass die Seite neu geladen wird.
11
12
Mehr zur Ajax-Architektur und die ersten Ajax-Beispiele finden Sie in Teil 2 des Buches.
13
INFO
29
ndex
Die komplette Steuerung von Ajax-Aufrufen übernimmt JavaScript. Das heißt, eine Ajax-Anwendung ist immer auch eine JavaScript-Anwendung. Und das hat schließlich dazu geführt, dass JavaScript heute wesentlich
Wissenswertes
mehr Beachtung findet als noch in der Vor-Ajax-Zeit – und das, obwohl sehr viele Web 2.0-Effekte schon sehr lange möglich gewesen wären. Abbildung 1.2: Die AjaxArchitektur
Server
Nutzer Browser
Anfrage an URL
HTML mit JS
JavaScriptInterpreter + XMLHttpRequest
Antwort
Anfrage Antwort als String oder mit XML
Webserver . HTML mit JS
Serverseitige Technologie .php, .aspx etc.
JavaScript war früher und ist heute im Web auch für die verschiedensten anderen Aufgaben verantwortlich: für Rollover-Effekte, für das Ändern mehrerer Frames mit einem Link, für die Vollständigkeitsüberprüfung von Formularen, für Navigationselemente und Animationen. All diese Anwendungen erfüllen hauptsächlich einen Zweck: Webseiten zu verbessern, interaktiver zu gestalten und komfortabler zu machen. Und sie sind ähnlich wie der Ajax-Aufruf oft integraler Bestandteil von Web 2.0-Anwendungen. Zum Erfolg von JavaScript hat sicherlich auch beigetragen, dass es clientseitig nahezu konkurrenzlos ist. Im Internet Explorer können Sie zwar noch VBScript (Visual Basic Script) verwenden, allerdings bleibt es auf den Internet Explorer beschränkt und schließt damit viele Browser aus.
1.1
Historie
Vor dem Erfolg von JavaScript stand eine wechselhafte Geschichte: Der erste kommerzielle Webbrowser wurde im Jahr 1995 von der Firma Netscape1 entwickelt. Relativ bald darauf entstand die Idee für eine Skriptsprache. Sie sollte im Browser, aber auch in den Webserver-Produkten von Netscape eingesetzt werden und trug den Namen LiveScript. Der Leiter des damaligen Projektteams war Brendan Eich, der als Vater von JavaScript gilt. Zu diesem Zeitpunkt wurde Java, eine recht komplexe Programmiersprache von Sun, sehr erfolgreich. Netscape dachte sich, die neue Skriptsprache könnte von diesem Erfolg profitieren, schloss eine Vereinbarung mit Sun und benannte LiveScript in JavaScript um. Dieser Schritt war ein Fehler, der heute noch seine Schatten wirft. JavaScript wird in vielen Diskussionen, Beiträgen und Pamphleten mit Java 1
30
Gegründet als Mosaic Communications von Jim Clark, der bereits mit Silicon Graphics ein erfolgreiches Unternehmen aus der Taufe gehoben hatte, und Marc Andreessen, einem Studenten in Stanford, der den ersten grafischen Webbrowser, Mosaic, entwickelt hat. Aus Mosaic entstand auch der erste Netscape Navigator.
Historie
gleichgesetzt, was in keinster Weise richtig ist. Die Syntax von JavaScript mag Ähnlichkeiten mit der von Java haben, da aber Java schon auf C und C++ basiert, kann man solche Ähnlichkeiten zwischen fast allen heute verwendeten Skript- und Programmiersprachen finden. Dankenswerterweise konnte sich JavaScript freischwimmen und hat seinen Erfolgsweg trotzdem eingeschlagen.2 Microsoft merkte spätestens beim erfolgreichen Börsengang von Netscape, dass das Web und der Browser große Zukunftschancen haben, und zog deswegen mit einem eigenen Browser, dem Internet Explorer, nach.
1 2
Die ersten ernst zu nehmenden Versionen des Internet Explorers sind zwei Auslieferungen der Version 3. Sie besitzen einige JavaScript-Fähigkeiten beispielsweise für Bilder. Leider trug nun auch Microsoft zur Namenskonvention von JavaScript bei: Aus lizenzrechtlichen Gründen nannte Microsoft seine JavaScript-Version JScript, was heute für ebenso viel Verwirrung sorgt.
1.1.1
3
4
Browserkrieg 5
In der vierten Version der Browser hat JavaScript (und JScript)3 einen deutlichen Sprung gemacht. Die Objektmodelle der Browser wurden erweitert (siehe Kapitel 8 »Document Object Model«) und eine Vielzahl an Funktionen kamen hinzu.
6
Das Problem war damals jedoch, dass beide Browser in vielen Bereichen völlig unterschiedliche Wege gingen. Erste Standardisierungsbemühungen der ECMA für JavaScript und des W3C (World Wide Web Consortium) für das Objektmodell (DOM = Document Object Model) waren zwar schon auf dem Weg, aber in der Hitze des Browsergefechts ging einiges unter. Diese Zeiten waren hart für JavaScript-Programmierer. Browserunterschiede waren noch wichtiger als heute. Aktuell kann man wenigstens auf eine Unterstützung der dritten Generation, also Internet Explorer 3 und Netscape Navigator 3, verzichten.
7
8
9
Ähnlich hart wurde es für Netscape: Microsoft zog mit fairen und weniger fairen Mitteln am Wettbewerber vorbei und Netscape ging schließlich in AOL Time Warner auf.
10
Der Quellcode von Netscape wurde freigegeben. Daraus entwickelte sich die Gecko-Engine als Basis und das zugehörige Mozilla-Projekt als Organisationsform. Der schlanke Browser Firefox aus dem Mozilla-Projekt hat sich zum Erfolgsmodell entwickelt und dem Internet Explorer über die Jahre ordentlich Marktanteile abgenommen. Von einem zweiten Browserkrieg zu sprechen, ist zwar übertrieben, aber immerhin wurde Microsoft gezwun-
11
12
13 2 3
Java ist zur gleichen Zeit von den Clients mehr und mehr verschwunden. Java-Applets finden sich im Web kaum noch. Dafür feiert Java in verschiedenen Formen serverseitig neue Erfolge. JavaScript schließt im Folgenden und im ganzen Buch JScript mit ein. Ist speziell von der MicrosoftImplementierung die Rede, dann wird es JScript genannt.
31
Wissenswertes
gen, das erste Mal seit Jahren seinen Browser weiterzuentwickeln. Das Resultat ist die aktuelle Version 7, die allerdings bei Webentwicklern durchaus noch auf einige Kritik stößt.
1.1.2
Heute
Der Browsermarkt im Jahr 2007 ist nach wie vor vom Internet Explorer beherrscht. Allerdings hat sich der Firefox je nach Statistik einen Anteil von 15 bis 40 % gesichert. Webhits sieht ihn aktuell bei knapp 25% (http://www.webhits.de/deutsch/index.shtml?webstats.html). Verbunden mit neuen modernen Browsern führt das zu einer größeren Vielfalt am Markt. Der Opera hat seinen dritten Platz verloren, kann sich aber dank kostenlos abgegebener Version nach wie vor halten. Den größten Sprung auf Platz drei hat der Safari für Mac OS X gemacht. Er wurde aus dem für die Benutzeroberfläche KDE für Linux entwickelten Konqueror abgeleitet. Oder um genauer zu sein, Apple hat die Engine des KDE, KHTML, als Basis für den eigenen Browser verwendet. Mittlerweile sind allerdings einige Änderungen aus dem Safari wieder zurück in das Projekt geflossen. Dieses Buch hilft Ihnen mit vielen Tabellen über die Browserkompatibilität, auch für die weniger populären Browser zu entwickeln. REF
Außer den Entwicklungen im Browserbereich gibt es in JavaScript natürlich noch weitere Strömungen: Die nächste JavaScript-Version könnte einen anderen Weg bei der Objektorientierung verfolgen, liegt aber noch deutlich in der Zukunft.
1.2
ECMAScript
ECMAScript ist nicht gleich JavaScript. ECMAScript definiert einen großen Teil des Sprachkerns von JavaScript und damit auch von JScript. JavaScript erweitert diesen Sprachschatz allerdings um einige Funktionen und Möglichkeiten. Sie finden den ECMAScript-Standard unter http://www.ecma-international. org/publications/standards/ECMA-262.htm. INFO
Der Sprachkern selbst ist nicht dazu gedacht, mit der Webseite und den HTML-Inhalten darin zu kommunizieren. Dazu benötigt JavaScript die Browserobjekte und das Object Model des Browsers. Die Standardisierung dieses Modells (DOM) obliegt dem W3C.4 Das Modell liefert die Objekte, die den Zugriff auf die Elemente einer Webseite, wie beispielsweise Bilder, Links oder Formularelemente, erlauben. 4
32
Siehe Kapitel 8 »Document Object Model«.
Weitere Einsatzgebiete
Die folgende Tabelle gibt eine Übersicht über die Parallelen zwischen dem ECMAScript-Standard und den JavaScript- bzw. JScript-Versionen. JavaScript-Version
JScript-Version
ECMAScript
1.0
1.0
1.1
1.1
1.2
3.0a
v1 (eingeschränkt)
1.3
5.0
v1
5.5 5.6
v3
Tabelle 1.1: JavaScript und ECMAScript
1 2
1.4b 1.5
3
1.6
4
1.7 a. b.
Den Zwischenschritt JScript 4.0 hat Microsoft ausgelassen. Nur in Netscape-Webservern, aber in keinem Browser implementiert.
5 In Abschnitt 1.8.1 finden Sie weitere Informationen zu den Browsern und den JavaScript-Versionen.
6
REF
1.3
Weitere Einsatzgebiete
7
Das Haupteinsatzgebiet von JavaScript liegt eindeutig im Browser und damit in der clientseitigen Verwendung. Allerdings ist JavaScript oder der übergeordnete Standard ECMAScript entgegen der landläufigen Meinung auch in anderen Bereichen anzutreffen: ■
■
8
serverseitig in ASP (Active Server Pages), einer serverseitigen Technologie von Microsoft. Allerdings wurde ASP mittlerweile weitgehend durch den Nachfolger ASP.NET abgelöst. serverseitig in ASP.NET, dem Nachfolger von ASP. Hier heißt JavaScript JScript.NET, wird allerdings im Vergleich zu C# und VB.NET selten eingesetzt.
9
10
11 Die Diskussion, welche Aufgaben besser clientseitig und welche besser serverseitig erledigt werden, finden Sie in diesem Buch noch häufiger. Außerdem benötigen Sie eine serverseitige Technologie für Ajax. Nicht behandelt wird die Frage, welche serverseitige Technologie die beste ist. Hier spielen viele Faktoren eine Rolle. Die Grabenkämpfe – gerade zwischen Microsoft und der OpenSource-Gemeinde rund um PHP – sind eine teilweise sehr emotionale Diskussion.
INFO
12
13
33
Wissenswertes
■
■
als Skript-Sprache für Technologien wie beispielsweise SVG (Scalable Vector Graphics), einen Vektorgrafikstandard des Internet-Standardisierungsgremiums W3C.5 Flash von Adobe, der Quasistandard und Platzhirsch bei Vektorgrafiken und -animationen, verwendet eine Skriptsprache namens ActionScript. Sie ist – wie JavaScript – seit Flash 5 ein Abkömmling von ECMAScript. Wer das eine beherrscht, kann auch das andere lernen.
1.4
JavaScript und Sicherheit
Einige Dinge kann clientseitiges JavaScript nicht: ■ ■ ■ ■
beliebig Browserfenster schließen Netzwerkverbindungen herstellen oder verarbeiten Dateien öffnen und speichern Grafiken zeichnen6
Der Grund für diese »Mängel« ist die Sicherheit. Niemand will einem Skript in einer fremden HTML-Seite das Recht geben, Dateien zu löschen. Diese strengen Sicherheitsregeln führen dazu, dass JavaScript verhältnismäßig wenig negative Schlagzeilen produziert. Die meisten Sicherheitslücken für den Internet Explorer wurden von ActiveX und damit VBScript gemeldet. Auch in Netscape gab es in den älteren Versionen 2 und 3 einige Probleme. Mehr über die Sicherheitsvorkehrungen von JavaScript erfahren Sie in Kapitel 30 »Sicherheit«. REF
1.5
JavaScript-Einbau
Clientseitiges JavaScript erweitert HTML. Dementsprechend sind die JavaScript-Skripte auch Teil der HTML-Seite. Sie sind im normalen HTML-Code eingebunden und werden vom Server auf den Client übertragen und dort ausgeführt. Damit der Browser erkennt, dass es sich um JavaScript und nicht um HTML handelt, müssen die JavaScript-Bereiche innerhalb einer HTML-Seite hervorgehoben sein.
5 6
34
Siehe http://www.w3.org/Graphics/SVG. Eine Ausnahme ist das dynamische Generieren von HTML-Elementen.
JavaScript-Einbau
So sieht ein normales HTML-Grundgerüst ohne JavaScript aus: Listing 1.1: Das HTML-Grundgerüst (grundgeruest.html) HTML-Grundgerüst
In den meisten einfachen Skripten in diesem Buch fehlt der Doctype am Anfang des Skripts. Für komplexere Anwendungen sollten Sie ihn allerdings immer verwenden, damit auch CSS sauber gerendert wird (und nicht im Quirks-Modus). Für JavaScript und Ajax selbst ist der Doctype dagegen unerheblich.
1 2
3 INFO
4
In diesem Buch kommt recht viel HTML vor. Sie sollten also mit HTML in Grundzügen vertraut sein. Die Beispiele sind – so weit möglich und sinnvoll – XHTML-konform:7 ■ ■
■ ■
5
Alle Tags sind Kleinbuchstaben. Jedes Tag wird beendet, auch Tags ohne Ende-Tag:
6
statt:
7
Attribute sind in Kleinbuchstaben geschrieben. Attributwerte stehen immer in Anführungszeichen.
8
JavaScript-Skripte werden in HTML mit und eingebunden. Zwischen diesen Tags steht der JavaScript-Code. Da die -Tags auch von anderen Skriptsprachen verwendet werden, muss mit dem Attribut language die Sprache identifiziert werden.
9
10
Wenn Sie das language-Attribut weglassen, verwenden die aktuellen Browser dennoch JavaScript als Standard. Da dies allerdings nicht selbstverständlich ist, sollten Sie sich nicht darauf verlassen.
11 TIPP
12
13
7
Zu finden unter: http://www.w3.org/TR/xhtml1/.
35
Wissenswertes
Das -Element findet seinen Platz im Kopf der HTML-Seite: Listing 1.2: Skript-Container im Kopf der HTML-Seite (script.html) Der Skript-Container document.write("Herzlich willkommen!");
Abbildung 1.3: Das Skript gibt einen Text aus.
INFO
Der Befehl document.write() gibt einen Text aus, der in Anführungszeichen innerhalb der runden Klammern steht. Eng verwandt ist document. writeln(), das eine einzelne Zeile ausgibt.
1.5.1
Alte Browser und deaktiviertes JavaScript
Browser ohne JavaScript-Unterstützung sind ein ständig wiederkehrendes Problem für JavaScript-Programmierer. Prinzipiell lassen sich zwei Fälle unterscheiden: ■
Abbildung 1.4: Mit deaktivierter JavaScript-Unterstützung zeigt ein JavaScript-fähiger Browser nichts an.
36
Browser, in denen JavaScript deaktiviert wurde. Sie ignorieren den Code in den -Tags (siehe Abbildung 1.4).
JavaScript-Einbau
■
Alte Browser, mobile Browser oder Spezialbrowser, die kein JavaScript unterstützen, kennen -Tags nicht. Daher ignorieren sie die Tags und geben (unter Umständen) den Inhalt aus. Hier kann es also passieren, dass das ganze Skript in Klarsicht ausgegeben wird.
Der erste Fall – deaktivierte JavaScript-Unterstützung – lässt sich nur dadurch umgehen, dass Ihre Website auch ohne JavaScript navigierbar ist.
1
Im zweiten Fall müssen Sie auf jeden Fall verhindern, dass das Skript ausgegeben wird. Dazu gibt es einen Trick: Der ganze Skriptteil wird in HTMLKommentare eingeschlossen. Damit JavaScript nicht über das schließende Kommentarzeichen --> stolpert, steht davor ein JavaScriptKommentarzeichen // für einen einzeiligen Kommentar.
2
3
Das folgende Beispiel illustriert diese Methode: Listing 1.3: Kommentare verhindern Fehler in alten Browsern (alte_browser.html)
4
Alte Browser
5
6
7
Es gibt auch neuere Browser, die kein JavaScript unterstützen, beispielsweise die auf mobilen Endgeräten wie PDAs. Für diese Browser können Sie einen -Container angeben.
8
Dieser -Container wird auch von Browsern mit deaktiviertem JavaScript ausgewertet und ausgegeben (siehe Abbildung 1.5).
9
Listing 1.4: Der -Container (noscript.html)
10
noscript Ihr Browser kann kein JavaScript
11
12
13
37
Wissenswertes
Abbildung 1.5: Der Firefox gibt den Inhalt des Containers aus, da die JavaScriptUnterstützung deaktiviert ist.
Abbildung 1.6: Mit aktiviertem JavaScript ignoriert der Firefox den Container.
Den -Container finden Sie nicht in den Beispielen dieses Buches, da er den Code unnötig aufblähen würde. INFO
1.5.2
Position des Skript-Bereichs
Die Position des -Containers sollte ursprünglich auf den Kopfbereich der HTML-Seite beschränkt bleiben. Bald aber wurde klar, dass insbesondere bei der Ausgabe von Text und Informationen ein -Bereich im Körper an der Stelle sinnvoll ist, an der die Ausgabe erfolgen soll. Listing 1.5: Skript im Körper der HTML-Seite (script_body.html) Skript im Body
Herzlich willkommen ohne JavaScript
Wo ein Skript-Container, ist ein zweiter auch nicht weit. Vor allem die Kombination aus einem Skript-Container im Kopf und im Körper ist sehr sinn-
38
JavaScript-Einbau
voll. Im Kopf können beispielsweise Funktionen8 definiert sein, die dann im Körper aufgerufen werden. Listing 1.6: Skript in Kopf und Körper der HTML-Seite (script_beide.html) Skript in Head und Body
1 2
3
4 Abbildung 1.7: Zuerst erfolgt die Ausführung des Skripts im Kopf und dann im Körper.
5
6
7
8
Eine dritte Möglichkeit besteht darin, zwei oder mehr Skripte im Kopf oder im Körper der HTML-Seite zu notieren. Zahl und Positionierung unterliegen hier also keiner Grenze:
9
Listing 1.7: Zwei Skripte im Körper der HTML-Seite (script_beide_body.html) Zwei Skripte im Body Hallo von HTML 8
10
11
12
13
Funktionen sind Codeanweisungen, die jederzeit mit einem Namen aufgerufen werden können (siehe Kapitel 4 »Funktionen«).
39
Wissenswertes
Abbildung 1.8: Zuerst eine JavaScript-Ausgabe, dann eine HTMLAusgabe und anschließend wieder JavaScript
INFO
Moderne Browser kommen auch mit einem Skript-Bereich zwischen Kopf und Körper der HTML-Seite zurecht. Allerdings sollten Sie dies der Ordnung halber vermeiden. Kurzform Der Skript-Container war in unseren Beispielen immer relativ lang. Eine Zeile reicht allerdings auch aus (hier ohne language-Attribut): document.write("Hallo");
TIPP
Im Allgemeinen sollten Sie derartige Kurzformen meiden, da der Code dadurch unübersichtlicher wird und die HTML-Kommentare für alte Browser bei Kurzformen außen vor bleiben.
1.5.3
Externes JavaScript
Bis jetzt war der JavaScript-Code immer Teil der HTML-Seite. Er lässt sich aber auch in einer eigenen Datei speichern. Eine Datei mit JavaScript-Code hat im Allgemeinen die Dateiendung .js. Im externen Skript werden nur die JavaScript-Befehle notiert: Listing 1.8: Ein externes Skript (extern.js) document.write("Herzlich willkommen!");
Der Aufruf des externen Skripts erfolgt mit dem Attribut src9 im Tag:
Soll das externe Skript extern.js, das im gleichen Verzeichnis wie die HTML-Datei liegt, eingebunden werden, sieht die entsprechende Webseite wie folgt aus:
9
40
Der Verweis auf das externe JavaScript kann relativ (z.B. .../skripte/extern.js) oder absolut (http://www.mut.de/skripte/extern.js) erfolgen.
JavaScript-Einbau
Listing 1.9: Ein externes Skript einfügen (skript_extern.html) Externes Skript einbinden
1 2
Abbildung 1.9: Die Ausgabe mit dem externen Skript funktioniert.
3
4
5
6 Sie sehen im Skript noch eine Anweisung innerhalb des -Containers. Alle Browser, die mindestens JavaScript 1.1 beherrschen – das sind alle Browser ab Netscape 3 und Internet Explorer 3 in der zweiten Version – ignorieren die Anweisung im Skript-Container, wenn das src-Attribut vorhanden ist. Die früheren Browserversionen, also hauptsächlich Netscape 2, konnten mit externen Skripten noch nichts anfangen. Sie führen nur die Anweisungen im Skript-Container aus. Glücklicherweise müssen Sie sich um so alte Boliden nicht mehr kümmern.
7
8
9 Abbildung 1.10: Netscape 2 führt die Anweisung im -Container aus, da er mit dem externen Skript nichts anfangen kann.
10
11
12
13
41
Wissenswertes
Die Verwendung von externen Skripten hat einige Vorteile: ■ ■
■ ■
Externe Skripte sind an zentraler Stelle verwaltbar. Jede Webseite kann auf ein externes Skript zugreifen. Häufig verwendete Funktionen und Objekte können dort also ausgelagert werden. Ein Zugriff ist von jeder Webseite aus möglich. Externe Skripte sind nicht ganz so einfach aus dem Quelltext zu kopieren wie JavaScript-Code direkt in der Webseite. Externe Skripte müssen nicht von demselben Webserver aufgerufen werden. Sie können auch mit einem absoluten Link auf ein Skript eines anderen Webservers verweisen.
Die Nachteile sollen allerdings auch nicht verschwiegen werden: ■
■
■
Beim Zugriff auf ein externes Skript muss eine Verbindung über das Internet (oder Intranet) aufgebaut werden. Das kostet Performance. Der Verlust gleicht sich aus, wenn mehrere Webseiten auf dasselbe Skript zugreifen, da der Browser es in den Cache übernimmt. Beim Programmieren müssen immer zwei Dateien kontrolliert werden. Dies macht sich negativ bemerkbar, wenn der JavaScript-Code häufig auf HTML-Elemente der Webseite zugreift. In Uraltbrowsern wie dem Netscape Navigator 2 und dem Internet Explorer vor 3.02 funktionieren externe JavaScript-Dateien gar nicht. Für den Internet Explorer 3.02 und 3.03 muss der MIME-Typ des Webservers für Dateien mit js-Endung auf APPLICATION/X-JAVASCRIPT gesetzt sein. Das ist allerdings heute in der Praxis ohne Bedeutung.
Als Fazit lässt sich ziehen, dass häufig und von mehreren Webseiten benötigte Funktionen in externe Skripten ausgelagert werden sollten. Seitenspezifische Anwendungen wie Ausgaben, Formularüberprüfungen usw. sind dagegen besser direkt in der Webseite aufgehoben. Speichern Sie wichtige Funktionen in externen Skripten und verwenden Sie für seitenspezifische Aufgaben einen zweiten Skript-Container in der Webseite.
INFO
In diesem Buch finden Sie Skripte hauptsächlich direkt in der Webseite. Das drückt keine Vorliebe des Autors aus. Der Grund dafür ist, dass Webseite und Skript nicht in zwei Listings abgedruckt werden müssen. Durch die Hervorhebungen bleibt das Geschehen im Skript dennoch immer übersichtlich.
1.5.4
MIME-Type
Das language-Attribut ist dem W3C-Standardisierungsgremium von HTML 4.0110 nicht gut genug. Das Problem besteht darin, dass die Liste der Sprachen, die für das language-Attribut zugelassen sind, nicht festgeschrieben ist. Daher wird angeraten, die Identifikation über den so genannten MIME10 Zu finden unter http://www.w3.org/TR/html4/.
42
JavaScript in Links
Type (Multipurpose Internet Mail Extensions) vorzunehmen. Für den MIME-Type wird das HTML-Attribut type verwendet. Für JavaScript sieht die Identifikation so aus:
Die Standards zum MIME-Type werden von der IETF (Internet Engineering Task Force) verwaltet. Die beiden relevanten Dokumente sind RFC2045 (http://www.ietf.org/rfc/rfc2045.txt) und RFC2046 (http://www.ietf. org/rfc/rfc2046.txt).
1 INFO
2
Da das language-Attribut bisher jedoch weiter verbreitet ist, wird es auch in diesem Buch eingesetzt.
3
Wollen Sie jedoch auch den MIME-Type unterbringen und verwenden nur eine Skriptsprache in einer Webseite (was meist der Fall ist), können Sie auch ein -Tag in den Kopf der HTML-Seite schreiben:
4
In diesem Fall können Sie language oder type sogar weglassen. Sinnvoller ist es allerdings, dennoch das language-Attribut zu verwenden, falls ein Browser-Exot mit dem -Tag für Content-Script-Type nichts anfangen kann.
1.6
5
6
JavaScript in Links
7
JavaScript-Code im -Container wird beim Laden der Seite ausgeführt. Einzige Ausnahme ist Code innerhalb einer Funktion (siehe Kapitel 4 »Funktionen«). Eine Funktion enthält Anweisungen und kann jederzeit mit ihrem Namen aufgerufen werden.
8
Das sofortige Ausführen von Code ist manchmal sinnvoll. In vielen Fällen soll allerdings auf Nutzereingaben gewartet werden. Eine Methode besteht darin, JavaScript-Code in einen Link zu schreiben.11 Dafür wird im hrefAttribut statt eines der normalen Protokolle – beispielsweise http: für absolute Links auf Webseiten oder mailto: für E-Mail-Links – javascript: verwendet. Dahinter dürfen ganz normale JavaScript-Anweisungen stehen.
9
10
Linktext
11
Im folgenden Beispiel gibt der Link per JavaScript einen Text aus: Listing 1.10: Ein JavaScript-Link (javascript_link.html)
12
JavaScript in Links
13
11 Man spricht hier von JavaScript-Links.
43
Wissenswertes
Bitte Klicken
INFO
Wenn Sie Anführungszeichen ineinander verschachteln, müssen die Anführungszeichen unterschiedlich sein, also innen einfache und außen doppelte oder umgekehrt:
HALT
JavaScript-Links haben einen großen Nachteil: In Browsern ohne JavaScript oder mit deaktivierter JavaScript-Unterstützung funktionieren die Links nicht. Sie sollten JavaScript-Links also nur einsetzen, wenn Sie gleich zu Beginn auch eine JavaScript-freie Version Ihrer Seite anbieten. Ansonsten sind Event-Handler besser geeignet (siehe Kapitel 9 »Ereignisse und EventHandler«).
Abbildung 1.11: Beim Anklicken des Links wird per JavaScript ein Text ausgegeben.
TIPP
In JavaScript-Links können Sie auch Funktionen aufrufen, die Sie vorher im -Container definiert haben. Mehr Informationen zu Funktionen erhalten Sie in Kapitel 4 »Funktionen«.
1.7
JavaScript bei Ereignissen (Event-Handler)
Mit JavaScript-Links lässt sich JavaScript-Code durch Anklicken eines Links ausführen. Wenn der Nutzer mit einer Webseite interagiert, treten allerdings noch wesentlich mehr Ereignisse auf. Beispielsweise fährt der Nutzer mit der Maus über einen Link oder er ändert etwas in einem Textfeld. Auch bei diesen Ereignissen haben Sie die Möglichkeit, JavaScript-Code aufzurufen. Dazu dienen Event-Handler.12
12 Eingedeutscht: Ereignis-Behandler. Wir verwenden Event-Handler, da es sich um den üblicheren Begriff handelt.
44
JavaScript bei Ereignissen (Event-Handler)
Ein Event-Handler ist ein HTML-Attribut. Er beginnt immer mit on, dann folgt der Name des Ereignisses: onclick steht also für einen Mausklick, onmouseover für das Überfahren eines Objekts mit der Maus. Event-Handler können in verschiedene HTML-Tags eingebaut werden. Eines der häufigsten Einsatzgebiete sind Links:
1
Listing 1.11: Event-Handler im Link (event_handler.html) Event-Handler Bitte Klicken
2
3
4
Hier wird der Event-Handler immer zuerst ausgeführt (siehe Abbildung 1.12). Wenn im JavaScript-Code nicht bereits eine Weiterleitung aktiviert ist, kommt allerdings der Link zum Tragen.
5
Wollen Sie die Weiterleitung auf jeden Fall verhindern und auf derselben Webseite bleiben, sollte das href-Attribut nur ein Doppelkreuz13 (#) enthalten. Das Doppelkreuz ist das Symbol für einen HTML-Anker. Folgt dahinter kein Ankername, wird die Seite mit dem Link aufgerufen:
6
Bitte klicken
7 Abbildung 1.12: Beim Anklicken des Links erscheint das Fenster mit der Warnmeldung.
8
9
10
11
12 Dieser Trick wird in der Praxis sehr häufig verwendet, da so zum einen in älteren Browsern keine Fehlermeldung erscheint wie bei JavaScript-Links, zum anderen aber auch nicht auf eine andere Seite gesprungen wird. Aller-
13
13 Auch Hash oder Raute.
45
Wissenswertes
dings scrollt der Browser häufig an den Anfang der Seite, was bei längeren Seiten unpraktisch ist.
HALT
Event-Handler können in HTML beliebig mit Groß- und Kleinbuchstaben geschrieben werden, da HTML und damit auch HTML-Attribute nicht zwischen Groß- und Kleinschreibung unterscheiden.14 JavaScript dagegen erlaubt Event-Handler im JavaScript-Code nur in Kleinbuchstaben. Da XHTML auch Kleinbuchstaben vorsieht, ist die Variante mit Kleinbuchstaben vorzuziehen. Wichtige Event-Handler An dieser Stelle lernen Sie bereits einige Event-Handler kennen: ■ ■ ■ ■ ■ ■
onclick – ist ein Mausklick auf ein Objekt. onmouseover – der Mauszeiger fährt auf ein Objekt. onmouseout – der Mauszeiger verlässt das Objekt wieder. onchange – ein Formularfeld oder Objekt ändert sich. onload – wenn die Webseite geladen ist. Dieses Attribut ist meist Teil des -Tags. onunload – wenn der Nutzer die Webseite verlässt. onunload befindet sich auch meist im -Tag.
Ausführlichere Informationen zum Thema erhalten Sie in Kapitel 9 »Ereignisse und Event-Handler«. REF
1.8
Versionsunterscheidung
Das language-Attribut des -Tags bietet noch eine Überraschung: Es erlaubt die Angabe der JavaScript-Version:
Obige Zeile wird nur in Browsern ausgeführt, die JavaScript 1.3 oder höher unterstützen. Beachten Sie, dass zwischen dem Sprachnamen und der Versionsnummer im language-Attribut kein Leerzeichen steht. HALT
1.8.1
JavaScript-Versionen
Die Zuordnung anhand der JavaScript-Versionen ist – wie sollte es anders sein – nicht völlig trivial. Vor allem die beiden großen Browser Internet Explorer und Firefox/Netscape Navigator bereiten einige Schwierigkeiten.
14 Die Unterscheidung zwischen Groß- und Kleinschreibung heißt auch case-sensitive.
46
Versionsunterscheidung
Die folgenden Tabellen verschaffen einen Überblick darüber, welcher Browser welche JavaScript-Versionen unterstützt. Behalten Sie die Versionen im Hinterkopf, denn dies hilft beim Programmieren von plattformübergreifenden Skripten. JavaScript-Version
Jscript-Version
ab Internet Explorer-Version
1.0
1.0
3
1.1
1.1
3, zweite Versiona
1.2
3.0b
4.0
1.3
5.0
5.0
1.4
Tabelle 1.2: JavaScriptVersionen und der Microsoft Internet Explorer
5.5
5.5 (nur teilweise ECMA v3-kompatibel)
5.6
6.0 (ECMA v3-kompatibel)
1.6
nicht unterstützt
1.7
nicht unterstützt
2
3
in keinem Browser vorhanden
1.5
1
4
5 a. b.
Der Internet Explorer 3.0 ist in zwei Versionen erschienen: die erste mit JavaScript 1.0 und die zweite mit JavaScript 1.1. Den Zwischenschritt JScript 4.0 hat Microsoft ausgelassen.
6 JavaScript-Version
ab Netscape Navigator-Firefox-Version
1.0
NN 2.0
1.1
NN 3.0
1.2
NN 4.0 bis 4.05
1.3
NN 4.06 bis 4.8
1.4
in keinem Browser vorhanden
1.5
FF 1.0
1.6
FF 1.5
1.7
FF 2.0
JavaScript-Version
ab Browserversion
1.3
Konqueror ab 3.0
1.3
Opera 6.0a
1.5
Opera ab 7.0
a.
Tabelle 1.3: JavaScriptVersionen und Firefox bzw. Netscape Navigator
7
8
9
10 Tabelle 1.4: JavaScript und die alternativen Browser
Einschränkungen: http://www.opera.com/docs/specs/.
11
12
13
47
Wissenswertes
1.8.2
Browserunterscheidung
Das language-Attribut mit Versionsnummer lässt sich auch einsetzen, um die JavaScript-Version in verschiedenen Browsern zu unterscheiden. Allerdings ist die Erkennung nur sehr unsauber. In Kapitel 14 lernen Sie bessere Alternativen kennen. Das folgende Skript gibt die – vom Browser unterstützten – JavaScript-Versionen aus: Listing 1.12: Browserunterscheidung mit Sprachversionen (script_version.html) Welche Version?
48
Versionsunterscheidung
Die Ausgabe im Internet Explorer (siehe Abbildung 1.13) erstaunt ein wenig. Er unterstützt nur JavaScript bis Version 1.3. In Tabelle 1.2 wird ihm allerdings 1.5 nachgesagt. Was den Befehlsumfang betrifft, hat die Tabelle recht, allerdings behandelt Microsoft hier das language-Attribut sehr vorsichtig. Wenn Sie also wünschen, dass JavaScript-Code im IE 6 oder 7 ausgeführt wird, müssen Sie als höchste Version JavaScript 1.3 angeben.
1 Abbildung 1.13: Der Internet Explorer 7 behauptet von sich, er unterstütze kein JavaScript 1.4 und 1.5.
2
3
4
5
Abbildung 1.14: Firefox 2.0 kann alles.
6
7
8
9
10
11
12
13
49
Inhalt
2
Syntax und Variablen 1
2
Am Anfang und im Zentrum der JavaScript-Programmierung stehen Daten und Werte, unabhängig davon, ob Sie nur einen einfachen Text ausgeben möchten oder komplexe Algorithmen schreiben. Es handelt sich immer darum, Werte zu behandeln und zu ändern.
3
Daten werden in JavaScript – wie auch in anderen Programmiersprachen – in Variablen gespeichert, wenn sie weiterverwendet und verändert werden sollen. Daten, die direkt in Anweisungen geschrieben werden, nennt man dagegen Literale (bzw. Konstanten). Sie sind nicht veränderbar und treten nur einmal auf.
4
5
Ein Literal ist beispielsweise der Text »Alles Gute!« in der folgenden Textausgabe: 6
document.write("Alles Gute!");
In Abschnitt 2.2 »Variablen deklarieren« zeigen wir Ihnen, wie Sie Variablen erzeugen und mit ihnen arbeiten.
7 REF
2.1
8
Datentypen
Daten treten nicht einfach nur als Literale oder Variablen auf, sie unterscheiden sich auch aufgrund ihrer Art. Folgende Zeilen verdeutlichen das:
9
document.write(4); document.write("Text");
10
Die erste Zeile gibt 4 aus. 4 ist eine ganze Zahl (Number). Die zweite Zeile dagegen enthält Text. Der zugehörige Datentyp heißt String und wird in JavaScript immer in Anführungszeichen geschrieben.
11
Im Folgenden erhalten Sie eine Übersicht über alle Datentypen, die JavaScript unterscheidet. 12 In Abschnitt 2.3.5 wird erklärt, wie JavaScript die verschiedenen Datentypen intern behandelt und wie Sie den Datentyp eines Werts ändern. REF
51
ndex
13
Syntax und Variablen
2.1.1
Integer
Integer sind ganze Zahlen ohne Nachkommastellen, die sowohl positive als auch negative Werte annehmen können, also beispielsweise: -1 20 0
Die einzige Frage ist, wie klein oder groß die Zahlen werden dürfen. JavaScript stellt Integer von –253 (–9 007 199 254 740 992) bis +253 (+9 007 199 254 740 992) exakt dar. Kleinere oder größere Zahlen können zwar verarbeitet werden, aber sie verlieren die exakten Endziffern. In folgender Zeile hängen wir einfach 67 an die kleinste darstellbare Zahl an. document.write(-900719925474099267);
Der Interpreter rundet die drei Endziffern: -900719925474099300
HALT
INFO
Bei einigen Anwendungen, vor allem den Bit-Operatoren, ist der mögliche Wertebereich kleiner. Er umfasst dann nur 32 Bit, geht also von –231 (–2 147 483 648) bis +231–1 (2 147 483 647). Die JavaScript-Funktion1 isFinite() testet, ob eine Zahl noch endlich, also kleiner bzw. größer als die größte bzw. kleinste speicherbare Zahl in JavaScript ist. Wenn dies der Fall ist, wird als Ergebnis der Wahrheitswert (Boolean) true zurückgeliefert. Beachten Sie, dass die größte bzw. kleinste speicherbare Zahl nichts mit der größten bzw. kleinsten exakt darstellbaren Zahl zu tun hat. Sie kann wesentlich größer bzw. kleiner sein, weist dann jedoch Ungenauigkeiten bei den letzten Stellen hinter dem Komma auf. Auch für nur ungenau speicherbare Zahlen liefert isFinite() true. false erhalten Sie bei Strings, die nicht in Zahlen konvertierbar sind, bei NaN und Infinity (unendlich). Mehr zu isFinite() finden Sie in Abschnitt 4.3 »Globale Funktionen«. Hexadezimale Schreibweise Integer können auch anders geschrieben werden. Eine recht bekannte Möglichkeit ist die hexadezimale Schreibweise. Das hexadezimale System hat nicht die Basis 10 – wie unsere Zahlenschreibweise –, sondern die Basis 16. Das heißt, es gibt 16 Ziffern, und da unsere Ziffern von 0 bis 9 nicht ausreichen, werden die Zahlen 10 bis 15 von den Buchstaben A bis F dargestellt. In JavaScript stellen Sie den hexadezimalen Ziffern 0x voran, um sie zu kennzeichnen, also beispielsweise: 0xAF 1
52
Eine Funktion ist ein Block aus einer oder mehreren Anweisungen, die mit ihrem Namen aufgerufen werden können. isFinite() gehört zu den Funktionen, die schon in JavaScript vorhanden sind. Mehr zu Funktionen erfahren Sie in Kapitel 4 »Funktionen«.
Datentypen
Die Umrechnung ist recht einfach: Die erste hexadezimale Ziffer, A, wird mit 16 multipliziert, dann wird die zweite, F, addiert. Für das angegebene Beispiel sieht die Rechnung wie folgt aus: 10 * 16 + 15 = 175
Diese Ausgabe können Sie auch im Browser überprüfen, da hexadezimale Werte automatisch umgerechnet werden:
1
document.write(0xAF);
Das hexadezimale System wird gerne in HTML dazu verwendet, RGB-Farbwerte anzugeben. Für jeden der drei Farbkanäle bilden zwei hexadezimale Ziffern den jeweiligen Wert von 0 bis 255 ab. Die Reihenfolge ist dabei immer Rot, Grün und Blau. Folgender Farbwert entspricht dem RGB-Wert 255, 0, 0, der für reines Rot steht:
2 INFO
3
#ff0000
4
Oktale Schreibweise Die oktale Schreibweise wird vom ECMA-262-Standard nicht unterstützt, aber von einigen JavaScript-Interpretern zugelassen. In Tabelle 1.1 finden Sie eine Übersicht, die zeigt, dass alle Browser die oktale Schreibweise unterstützen. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
5
6 Tabelle 2.1: Die oktale Schreibweise
7
In der oktalen Schreibweise beginnen Zahlen immer mit einer 0. Anschließend folgen Ziffern von 0 bis 7. Für die oktale Schreibweise werden also nur 8 Ziffern verwendet, da die Basis 8 ist (oktal ist der griechische Begriff für acht). Die Umrechnung ist recht einfach:
8
9
0265
ergibt
10
2 * 64 + 6 * 8 + 5 = 181
Die Schwierigkeit bei oktaler Schreibweise liegt darin, ob die vorangestellte 0 für einen Dezimalwert oder die oktale Schreibweise steht. Gerade beim Umwandeln eines Strings in eine Zahl mit parseInt(String) steckt hier eine Fehlerquelle. Wenn Sie allerdings als zweiten Parameter das Zahlensystem angeben (parseInt(String, System) – wobei System für Dezimalzahlen den Wert 10 hat), erfolgt die Umwandlung exakt.
11 HALT
12
13
53
Syntax und Variablen
2.1.2
Gleitkommazahlen
Gleitkommazahlen (Float) haben Nachkommastellen, die durch einen Dezimalpunkt abgetrennt werden. Der Punkt verdeutlicht, dass JavaScript aus dem anglophonen Sprachraum kommt. Dort ersetzt der Dezimalpunkt das bei uns bekannte Dezimalkomma. 4.65
Erscheint vor dem Punkt keine Zahl, ist die Zahl vor dem Punkt eine 0: .848
steht also für 0,848. In JavaScript wird nicht zwischen Integern und Fließkommazahlen differenziert. Beide werden auch als Datentyp Zahl bezeichnet. INFO
Beachten Sie, wie schnell es geschehen kann, dass eine Gleitkommazahl auf einmal mit einem Komma statt einem Punkt im Skript landet. Der JavaScript-Interpreter ignoriert im Allgemeinen das Komma und gibt keinen Fehler aus, was die Fehlersuche sehr erschwert. Bei document.write() sind mehrere durch Kommata getrennte Parameter möglich. Die folgende Zeile document.write(0,40 + 4);
entspricht also document.write(0); document.write(40 + 4);
In Abbildung 2.1 sehen Sie, dass der Browser statt 4,4 als Ergebnis 044 anzeigt. Abbildung 2.1: Der Browser zeigt aufgrund des Kommas ein eventuell unerwünschtes Ergebnis an.
Gleitkommazahlen nehmen Werte von 10–324 bis 10308 an. Größere Werte werden unendlich (Rückgabe: Infinity). Exponentialschreibweise Neben der normalen Schreibweise existiert ebenfalls eine alternative Schreibweise mit Exponent. Zuerst folgt eine reelle Zahl, dann der Buchstabe E (oder e), ein Vorzeichen (plus oder minus) und eine ganze Zahl als 54
Datentypen
Exponent. Wird das Vorzeichen weggelassen, nimmt der JavaScript-Interpreter automatisch einen positiven Wert an. 2.2E2
steht also für 2,2 * 102 = 220. Da das Ergebnis ein Integer ist, bildet die Exponentialnotation genau genommen sowohl eine Schreibweise für Gleitkommazahlen als auch für ganze Zahlen. Wenn Sie eine Zahl in Exponentialschreibweise ausgeben, wandelt der JavaScript-Interpreter sie nur bis zu einer bestimmten Größe in die normale Schreibweise um.
1
2
INFO
9e+20
3
wird als 900 000 000 000 000 000 000 ausgegeben, 9e+21
nur als 9e+21.
4
Im negativen Bereich werden nur Exponenten bis –6 als normale Zahlen ausgegeben; ab –7 erfolgt Exponentialnotation.
5
2.1.3
Zeichenketten 6
Zeichenketten oder auch Strings2 enthalten Text, der allerdings durchaus auch aus Ziffern und anderen Zeichen bestehen kann. Sie erkennen einen String im Code immer an den Anführungszeichen.
7
"Text"
Ebenfalls ein String ist:
8
"12,30 Euro"
Die Anführungszeichen, die die Strings umgeben, können auch nur einfache Anführungszeichen sein:
9
'Text'
10
Der JavaScript-Interpreter erlaubt keine Mischung von einfachen und doppelten Anführungszeichen: document.write("Text');
HALT
11
zur Ausgabe eines Strings ist also verboten. Der Browser gibt idealerweise einen Fehler aus (siehe Abbildung 2.2). Der Internet Explorer 7 meldet hier allerdings beispielsweise im Gegensatz zu seinem Vorgänger nichts.
12
13 2
Da der Begriff String aus Programmierersicht die richtige Bezeichnung für den Datentyp ist, verwenden wir im weiteren Verlauf des Buches hauptsächlich diesen Anglizismus. Taucht der Begriff Zeichenkette auf, ist dies als Synonym zu String zu verstehen.
55
Syntax und Variablen
Abbildung 2.2: Die Fehlermeldung im Internet Explorer 6, wenn die Anführungszeichen nicht korrekt angegeben wurden
String-Länge Ein String darf in JavaScript eine unbegrenzte Länge haben. Allerdings sollten Sie den String nicht mit einem Zeilenumbruch im Texteditor zwischen den Anführungszeichen umbrechen, da dies zu einem Fehler führt.
TIPP
Einige sehr alte Browser unterstützen nur Strings mit maximal 255 Zeichen. Dies ist wichtig, wenn Sie eine 100-prozentige Abwärtskompatibilität anstreben. Escape-Sequenzen Wenn Sie in Strings Anführungszeichen oder Apostrophe (die geraden Anführungszeichen entsprechen) verwenden möchten, kann es Probleme geben. Folgender String ist problematisch: 'Ich denk's mir'
Der Interpreter nimmt an, das zweite gerade Anführungszeichen sei bereits das Ende des Strings. Um ihm zu signalisieren, dass das gerade Anführungszeichen keine JavaScript-Bedeutung hat, wird es mit einem Backslash (\)3 entwertet. 'Ich denk\'s mir'
Der Backslash ist das allgemein gültige Zeichen für die Entwertung von Zeichen. Den Backslash in Verbindung mit dem Zeichen nennt man EscapeSequenz (häufig auch Steuerelement).
3
56
Tastenkürzel (AltGr)+(ß).
Datentypen
In JavaScript gibt es einige standardmäßig verfügbare Escape-Sequenzen, die in Tabelle 2.2 aufgeführt sind. Wenn Sie ein Zeichen, das nicht entwertet werden müsste, mit dem Backslash (\) entwerten, hat dies keine Auswirkungen, da der Browser den Backslash einfach ignoriert. Wollen Sie dagegen einen Backslash in den Text einfügen, sollten Sie ihn als EscapeSequenz (\\) schreiben. 1 Escape-Sequenz
Beschreibung
\0
Nullzeichen; NUL. Alle Zeichen dahinter werden ignoriert. Das Nullzeichen terminiert den String.
\b
Backspace; entspricht der (Entf)-Taste, wird aber nicht von allen Browsern interpretiert.
\f
Seitenvorschub.
\n
Zeilenumbruch; beachten Sie, dass dieser Zeilenumbruch nicht funktioniert, wenn Sie den String auf einer HTML-Seite ausgeben. Hier benötigen Sie das HTML-Tag .
\r
Wagenrücklauf; stammt noch aus der Schreibmaschinenzeit. Der Cursor geht bis an den Anfang auf das erste Zeichen in der nächsten Zeile zurück.
\t
Tabulator.
\v
Vertikaler Tabulator; wird auch von neueren Browsern nicht unterstützt und ist daher zu meiden.
\\
Backslash; gibt einen Backslash aus.
Tabelle 2.2: Escape-Sequenzen von JavaScript
2 3
4
5
6
7 Einfaches Anführungszeichen.
\"
Doppeltes Anführungszeichen.
\xAA
Zeichen aus dem Latin-1-Zeichensatz, wobei die Zeichennummer durch den hexadezimalen Wert bei AA angegeben wird. (Latin-1-Zeichen können auch in oktaler Schreibweise mit drei Ziffern angegeben werden. Dies wird von den neueren Browsern unterstützt, ist aber nicht Teil der ECMA-262 v3.)
8
\uAAAA
9
Zeichen aus dem Unicode-Zeichensatz, wobei die Zeichennummer als hexadezimaler Wert mit vier Stellen (AAAA) angegeben wird (in Abschnitt 2.3.5 »Zeichensatz« erfahren Sie Details zu den Zeichensätzen in JavaScript).
10
Die besonderen Escape-Sequenzen für den Zeilenumbruch, den Wagenrücklauf und den Tabulator sehen Sie in JavaScript am einfachsten in einem Warnfenster, das mit alert() erzeugt wird.
11
12
Listing 2.1: Escape-Sequenzen im Einsatz (escape_sequenzen.html) Escape-Sequenzen
Abbildung 2.3: Die EscapeSequenzen in einem Warnfenster.
HALT
Bei einem Warnfenster oder Ähnlichem entscheiden sowohl der Browser als auch das Betriebssystem, wie Escape-Sequenzen angezeigt werden. Entsprechend gibt es einige Unterschiede, vor allem beim Zeilenumbruch: Der Netscape Navigator unter Windows stellt beispielsweise den Wagenrücklauf nicht als Zeilenumbruch dar. Er benötigt ein \n. Testen Sie also gut und verwenden Sie im Allgemeinen am besten nur \n!
Abbildung 2.4: Der Wagenrücklauf wird nicht angezeigt.
2.1.4
Boolean
Der Datentyp Boolean4 besteht nur aus zwei Zuständen: wahr oder falsch. Entsprechend heißt er auch Wahrheitswert. Wann benötigt man diesen Datentyp? Recht häufig, da in der Programmierung oftmals eine Bedingung überprüft wird, die dann wahr oder falsch zurückliefert. Ein Computer speichert Informationen in einem Bit. Dieses Bit kann ebenfalls nur zwei Zustände annehmen: 0 und 1. Entsprechend werden die beiden Wahrheitswerte manchmal auch als 0 (false = falsch) und 1 (true = wahr) geschrieben. JavaScript verwendet also entweder 0 für falsch und 1 für wahr oder alternativ die englischen Begriffe: true und false.5 var a = true; var b = false; 4
5
58
Benannt ist der Datentyp nach dem englischen Mathematiker George Boole, geboren am 2. November 1815 in Lincoln, England, und gestorben am 8. Dezember 1864 in Ballintempel, Irland. Er begründete die boolesche Algebra, die Logik mit einfachen algebraischen Symbolen für die Mathematik greifbar machte. In den Beschreibungen finden Sie im Folgenden sehr oft die Anglizismen true und false, da sie in JavaScript die entsprechenden Schlüsselwörter für Wahrheitswerte darstellen.
Datentypen
Im obigen Beispiel werden zwei Variablen definiert (siehe Abschnitt 2.2 »Variablen deklarieren«). Variable a hat den Wert true und Variable b den Wert false.
2.1.5
Objekte 1
Objekte – sowohl eingebaute als auch selbst definierte – sind für die Programmierung mit JavaScript sehr wichtig (siehe Kapitel 6 »Objekte in JavaScript«). Die komplexen Informationen, die in einem Objekt gespeichert werden, verlangen nach einem eigenen Datentyp: Object.6
2
Eigenschaften von Objekten, die Werte speichern, können einen beliebigen Datentyp, beispielsweise String oder Boolean, annehmen.
3 INFO
2.1.6
Arrays
4
Arrays speichern Datenelemente und vergeben einen Index, mit dem auf die Werte zugegriffen werden kann (siehe Kapitel 7 »Arrays und Strings«). Intern entspricht ein Array ebenfalls dem Datentyp Object.
2.1.7
5
Funktionen
6
Funktionen stellen Anweisungen bereit, die von beliebiger Stelle aus dem Skript aufgerufen werden können (siehe Kapitel 4 »Funktionen«). Sie sind in JavaScript Daten des Typs Object.
2.1.8
7
Spezielle Werte
8
Neben den einfachen (primitiven) Datentypen Zahl, String und Boolean und dem komplexen Datentyp Object gibt es noch einige spezielle Werte. Das sind eigentlich Datentypen, die nur einen Wert haben.
9
null 10
Der Wert null steht für einen Datensatz ohne Wert bzw. eine Variable ohne Zuweisung. Er ist in JavaScript nicht gleichbedeutend mit 0, auch wenn er manchmal so verwendet wird.
11
Sie erhalten beispielsweise 0, wenn Sie alle Elemente eines Arrays mit einer Schleife durchgehen (siehe Kapitel 7 »Arrays und Strings«) und das Ende des Arrays erreicht haben. Der nächste – nicht mehr vorhandene – Datensatz hat dann den Wert null.
12
13 6
Der Datentyp Object wird auch als komplexer Datentyp bezeichnet. Einfache (oder primitive) Datentypen sind Zahl (Integer und Fließkomma), String und Boolean.
59
Syntax und Variablen
undefined Wenn eine Variable zwar deklariert ist, aber keinen Wert zugewiesen bekommen hat, hat sie den Wert undefined. Ebenso ist eine Objekteigenschaft ohne Wert undefined. Mit diesem Wert können Sie also prüfen, ob eine Variable oder Eigenschaft überhaupt einen Wert hat.
HALT
Der Vergleichsoperator7 für Gleichheit in JavaScript (==) setzt null und undefined gleich. Intern handelt es sich allerdings um unterschiedliche Werte. Dies wird mit dem Operator Genau gleich (===) sichtbar. Er berücksichtigt auch den Datentyp der zu vergleichenden Elemente und liefert bei einem Vergleich zwischen null und undefined das Ergebnis false. Infinity Infinity steht für unendlich. Dieser Wert wird ausgegeben, wenn eine Rechnung oder Zahl in JavaScript nicht mehr dargestellt werden kann. Sie ist dann unendlich. Auch bei einer Division durch 0 taucht Infinity auf.
NaN NaN besagt, dass ein Wert keine Zahl ist. Dies wird beispielsweise ausgegeben, wenn bei einer Berechnung nicht nur der Datentyp Zahl, sondern beispielsweise ein String verwendet wird.
2.2
Variablen deklarieren
Um Variablen als Datenspeicher zu nutzen, müssen sie erst einmal erzeugt werden. Dieser Vorgang heißt Deklarieren. Der JavaScript-Interpreter erkennt an dem Schlüsselwort var, dass eine Variable deklariert werden soll: var x;
Diese Zeile deklariert die Variable x. Was geschieht beim Deklarieren? Der JavaScript-Interpreter erkennt, dass eine neue Variable angelegt werden soll. Er reserviert dann für die Variable Raum im Hauptspeicher und verknüpft diesen Platz mit dem Variablennamen, sodass er jederzeit darauf zugreifen kann, wenn die Variable wieder zum Einsatz kommt.
INFO
In JavaScript müssen Sie bei der Deklaration der Variablen keinen der soeben besprochenen Datentypen zuweisen. JavaScript ist nicht typisiert, das heißt, eine JavaScript-Variable kann jeden Datentyp speichern, im Laufe ihrer Lebensdauer sogar unterschiedliche. Mehr dazu erfahren Sie in Abschnitt 2.3.6 »Typkonvertierung«.
7
60
Siehe Abschnitt 3.1 »Operatoren«.
Variablen deklarieren
Ist eine Variable erst einmal deklariert, kann sie jederzeit mit ihrem Namen, in unserem Beispiel also x, aufgerufen werden. Ein einfacher Aufruf kann in der Ausgabeanweisung document.write() erfolgen: document.write(x);
Da die Variable x bisher noch keinen Wert hat, wird undefined ausgegeben. 1
Abbildung 2.5: Bisher ist die Variable noch undefined
2 3
4 Sie können auch mehrere Variable in einer Zeile deklarieren. Trennen Sie die Variablen mit Kommata:
5 INFO
var x, y;
definiert also x und y. Wenn Sie var weglassen, reserviert JavaScript für die Variable dennoch Platz im Hauptspeicher. Allerdings ist die Variable dann grundsätzlich global, das heißt im kompletten Skript verfügbar. Lokale Variablen sind auf eine Funktion beschränkt (siehe Abschnitt 4.1.6 »Lokale und globale Variablen«). Wenn Sie eine lokale Variable in einer Funktion deklarieren wollen und var vergessen, kann es unter Umständen zu Problemen kommen, vor allem wenn das Skript noch gleichnamige globale Variablen besitzt. Außerdem ist der Zugriff auf undeklarierte Variablen langsamer, weil global nach Variablen gesucht werden muss.
2.2.1
6
7 HALT
8
9
10
Werte zuweisen
undefined ist natürlich auf Dauer kein schöner Wert8 für eine Variable. Daher muss sie einen Wert erhalten. Dies erledigt in JavaScript das Istgleich-Zeichen (=).
11
var x = 3;
12
Diese Zeile weist der Variablen x den Wert 3 zu. Das Ist-Gleich heißt wegen seiner Funktion auch Zuweisungsoperator. Andere Operatoren finden Sie in Abschnitt 3.1 »Operatoren«.
8
13
Der Wert einer Variablen wird auch als Literal bezeichnet (siehe oben). Er tritt einmal auf. Ändert sich der Wert, handelt es sich um ein neues Literal.
61
Syntax und Variablen
Wird einer Variablen, wie im aktuellen Beispiel, bereits beim Deklarieren ein Wert zugewiesen, spricht man auch von einer Initialisierung der Variablen. Abgesehen vom Sprachgebrauch kann eine Variable natürlich auch erst später einen Wert erhalten: var x; x = 3;
Diese Zeilen bewirken dasselbe wie die vorhergehende Initialisierung: Die Variable x erhält den Wert 3.
INFO
Sollten Sie eine Variable häufiger mit dem Schlüsselwort var deklarieren, bleibt JavaScript gnädig. Wird bei der zweiten Deklaration ein Wert zugewiesen, übernimmt JavaScript den Wert als normale Zuweisung und ignoriert var. Erfolgt bei der ersten Deklaration eine Zuweisung, bei der zweiten dagegen nicht, behält die Variable den Wert aus der ersten Zuweisung. var x = 1; var x; document.write(x);
gibt also 1 aus.
2.2.2
Werte ändern
Die Werte von Variablen sind beliebig änderbar. Um einer Variablen einen neuen Wert zuzuweisen, verwenden Sie einfach wieder den Zuweisungsoperator (=). Im Gegensatz zum mathematischen Sprachgebrauch ist das Ist-Gleich hier kein Vergleich, sondern eine Wertzuweisung: var x = 3; x = 2;
Die Variable x erhält hier den Wert 3, der anschließend auf 2 geändert wird. Variablen sind aber noch mächtiger: Mit Berechnungen und Operatoren können Sie Variablen Rechnungsergebnisse zuweisen und sie miteinander verknüpfen. var x = 4 * 3; var y = x / 2;
INFO
62
Das Multiplikationszeichen (*) und das Divisionszeichen (/) sind so genannte arithmetische Operatoren. Sie und einige andere nützliche Operatoren lernen Sie in Abschnitt 3.1 »Operatoren« kennen.
Variablen deklarieren
2.2.3
Strichpunkt (;)
In den bisherigen Beispielen folgte nach jeder Variablendeklaration oder -zuweisung, aber auch nach jeder Ausgabe mit document.write() immer ein Strichpunkt (;).9 Der Strichpunkt beendet eine Anweisung in JavaScript. Muss er aber nicht. In anderen Sprachen, beispielsweise Java oder PHP, ist die Angabe des Strichpunkts Pflicht, der JavaScript-Interpreter dagegen meckert nicht, wenn Sie ihn am Zeilenende weglassen.
1
2
Die Beispiele in diesem Buch sind dennoch alle mit Strichpunkt ausgestattet.10 Dafür gibt es einen Grund: Anweisungen sehen wesentlich übersichtlicher aus, wenn sie beendet werden. Der Programmierstil wird sauberer.
3
Wollen Sie mehrere Anweisungen in eine Zeile schreiben, müssen Sie diese durch Strichpunkte trennen: var x, y; x = 2; y = 4;
4
HALT
5
Fügen Sie keine Strichpunkte an, übernimmt das der JavaScript-Interpreter automatisch. Dies ist bei Anweisungen über mehrere Zeilen problematisch. Im Folgenden sehen Sie die Rückgabe eines Werts in einer Funktion, über zwei Zeilen verteilt:
6
return x;
7
Der Interpreter nimmt aber an, dass die obere Zeile eine eigenständige Anweisung ist, und behandelt dies wie folgt:
8
return; x;
Mit diesen Zeilen wird aber nicht x zurückgegeben, sondern die Funktion verlassen, ohne dass eine Rückgabe erfolgt. Die zweite Zeile x; wird ignoriert.
2.2.4
9
10
Mehrere Variablen
Wenn Sie mehrere Variablen gleichzeitig, das heißt mit nur einem varSchlüsselwort, deklarieren wollen, trennen Sie sie durch Kommata:
11
var x, y, z;
definiert x, y und z als Variablen.
12
Das funktioniert auch mit Wertzuweisung: var x = 2, y = 4, z = 5;
13
9 Der Strichpunkt heißt auch Semikolon. 10 Wenn er hinter einer Anweisung fehlt, handelt es sich um ein Versehen des Autors. Eine kurze E-Mail an
[email protected] genügt, und der Fehler wird in der nächsten Auflage behoben.
63
Syntax und Variablen
2.3
Bezeichner und Typen
Bevor Sie mit Variablen zu arbeiten beginnen und sich ans Programmieren machen, sollten Sie noch einige Grundlagen über Variablen und JavaScript mit auf den Weg nehmen. Dieses Kapitel behandelt hauptsächlich die Variablennamen (auch Bezeichner genannt) und die Typkonvertierung in JavaScript.
2.3.1
Groß- und Kleinschreibung
JavaScript unterscheidet zwischen Groß- und Kleinschreibung.11 Dies gilt nicht nur bei Variablennamen, sondern auch bei Funktionsnamen, JavaScript-Programmierelementen und Objekten. var Test = 3;
ist also eine andere Variable als test = 4;
2.3.2
Variablennamen
Ein Variablenname darf in JavaScript aus Buchstaben, Zahlen und dem Unterstrich (_) bestehen. Beginnen darf er allerdings nur mit einem Buchstaben oder dem Unterstrich, nicht aber mit einer Zahl. Darüber hinaus sind die meisten Sonderzeichen im Variablennamen nicht erlaubt. Eine Ausnahme ist das Dollarzeichen $. Es wird von vielen Ajax-Frameworks verwendet, um Funktionen aufzurufen. Folgende Namen sind korrekt: var var var var
_Hallo; T_H_2; x9302; $funktion;
Nicht erlaubt sind dagegen: var Jo&Jo; var 90210; var 0acht;
Des Weiteren sind in Variablennamen die reservierten Schlüsselwörter von JavaScript verboten (siehe nächster Abschnitt). Was erlaubt ist, ist aber nicht unbedingt sinnvoll. Sie sollten sich angewöhnen, mit sprechenden Variablennamen oder einer schlüssigen Namenskonvention zu arbeiten. Besteht ein Variablenname aus mehreren Wörtern, trennen Sie ihn am besten mit Unterstrichen oder, indem Sie neue Wörter mit einem Großbuchstaben beginnen.
11 Dies wird auch als case-sensitive bezeichnet.
64
Bezeichner und Typen
var test_account; var testAccount;
Die zweite Variante gleicht auch der Konvention, die JavaScript bei seinen Methoden und Eigenschaften verwendet: Der Anfang – also das erste Wort – mit Kleinbuchstaben, neue Worte beginnen mit Großbuchstaben.
2.3.3
1
Reservierte Schlüsselwörter
JavaScript verwendet – wie jede andere Programmiersprache auch – bestimmte Wörter für Programmierkonstrukte. Beispielsweise gibt das Wort var dem JavaScript-Interpreter an, dass danach eine Variablendeklaration folgt. Diese Wörter heißen Schlüsselwörter.
2 3
Sie sind reserviert, das heißt, Variablenbezeichner und jede andere Art von Bezeichner12 dürfen nicht so benannt werden. Die Vorgaben zu reservierten Schlüsselwörtern beginnen mit Wörtern, die bereits in der Sprache verwendet werden. Sie dürfen auf jeden Fall nicht als Bezeichner eingesetzt werden. Zusätzlich gibt es häufig für die Zukunft vorgesehene Schlüsselwörter, die oftmals noch als Bezeichner funktionieren würden. Wenn es dann aber neue Browserversionen gibt, versagen die alten Skripten mit diesen Bezeichnern. Daher gilt grundsätzlich die Regel, keine Schlüsselwörter als Bezeichner in Skripten zu verwenden.
4
5
6
Die grundlegenden Vorgaben für JavaScript kommen offiziell von der ECMA. In der Spezifikation ECMAScript-262 v3 werden die in Tabelle 2.3 und Tabelle 2.4 aufgeführten Schlüsselwörter reserviert. break
else
new
var
case
finally
return
void
catch
for
switch
while
continue
function
this
with
default
if
throw
delete
in
try
do
instanceof
typeof
7 Tabelle 2.3: Reservierte Schlüsselwörter von ECMAScript262 v3
8
9
10
11
12
13
12 Beispielsweise Funktions- und Objektnamen.
65
Syntax und Variablen
Tabelle 2.4: Für die Zukunft reservierte Schlüsselwörter von ECMAScript262 v3
abstract
enum
int
short
boolean
export
interface
static
byte
extends
long
super
char
final
native
synchronized
class
float
package
throws
const
goto
private
transient
debugger
implements
protected
volatile
double
import
public
Mozilla folgt in der JavaScript-Version 1.5, 1.6 und 1.7 weitgehend den Vorgaben von ECMAScript-262 v3 (siehe Tabelle 2.5).13 Tabelle 2.5: Reservierte Schlüsselwörter (Mozilla JavaScript 1.5)13
abstract
delete
function
null
throw
boolean
do
goto
package
throws
break
double
if
private
transient
byte
else
implements
protected
true
case
enum
import
public
try
catch
export
in
return
typeof
char
extends
instanceof
short
var
class
false
int
static
void
const
final
interface
super
volatile
continue
finally
long
switch
while
debugger
float
native
synchronized
with
default
for
new
this
Microsoft hat die JScript-Referenz auch an JScript.NET angepasst (läuft auch unter dem Namen JScript 7). Daher gibt es noch einige neue reservierte Schlüsselwörter. Diese sind zwar eigentlich nicht alle für den clientseitigen Einsatz vorgesehen. Aus Gründen der Kompatibilität zu zukünftigen Browsern und JavaScript-Implementationen sollten Sie sie allerdings dennoch nicht als Bezeichner verwenden.
13
66
Quelle : http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Reserved_Words.
Bezeichner und Typen
14
break
case
catch
class
const
continue
debugger
default
delete
do
else
export
extends
false
finally
for
function
if
import
in
instanceof
new
null
protected
return
super
switch
this
throw
true
try
typeof
var
while
with
abstract
boolean
byte
char
decimal
double
enum
final
float
get
implements
int
interface
internal
long
package
private
protected
public
sbyte
set
short
static
uint
ulong
ushort
void
1515
Tabelle 2.6: Die reservierten Schlüsselwörter in JScript (mit JScript.NET)14
1
2 Tabelle 2.7: Neue reservierte Wörter15
3
4
5
6 assert
ensure
event
goto
invariant
namespace
native
require
synchronized
throws
transient
use
volatile
Tabelle 2.8: Für die Zukunft reservierte Schlüsselwörter in JScript
7
8 Ein genauer Blick auf die Listen verrät, dass undefined bei Mozilla und Microsofts JScript kein reserviertes Schlüsselwort ist, null dagegen schon.16 Der Grund dafür ist, dass undefined als globale Variable gesehen wird, deren Wert für alle Variablen und Eigenschaften ohne Wert gilt.
9 HALT
10
2.3.4
Formatierung
JavaScript ignoriert Leerzeichen und Tabulatoren zwischen Ausdrücken (auch Token). Ein Ausdruck kann eine Variable, ein Programmierkonstrukt oder ein Bezeichner für ein Objekt sein.
11
12
13 14 Quelle: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/jscript7/html/ jsreserved.asp. 15 Wörter dürfen aufgrund der Abwärtskompatibilität als Bezeichner eingesetzt werden. Empfehlenswert ist dies allerdings nicht, da spätere Browserversionen dies eventuell untersagen. 16 Außer in ECMAScript!
67
Syntax und Variablen
HALT
Leerzeichen und Tabulatoren in Ausdrücken, sprich beispielsweise ein durch Leerzeichen getrennter Variablenname, werden natürlich nicht ignoriert, sondern teilen den Ausdruck in mehrere. Für die Formatierung Ihrer Skripten sollten Sie also durchaus beispielsweise Leerzeichen verwenden. In diesem Buch werden die Zeilen für jede logische Skriptebene um jeweils zwei Leerzeichen eingerückt.
2.3.5
Zeichensatz
Bisher haben Sie bereits die Beschränkungen bei Bezeichnern für Variablen, Funktionen und Objekte und die Verwendung von Escape-Sequenzen bei Strings kennen gelernt. Abgesehen von diesen Einschränkungen unterstützt JavaScript in neueren Browsern den kompletten Unicode-Zeichensatz.17 Tabelle 2.9: Unicode
a.
INFO
NS4.x
M/FF
a
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Ab Netscape Navigator 4.0.6.
Unterstützt der Browser kein Unicode, wird normalerweise automatisch nur der ASCII-Zeichensatz unterstützt. In ECMAScript ist Unicode erst in v3 allgemein gültiger Standard (außer in Bezeichnern). Vorher waren reine UnicodeZeichen nur in Strings und Kommentaren (siehe Punkt 2.4.1 »Kommentare«) zugelassen. Der Unicode-Zeichensatz hat eine Tiefe von 16 Bit und enthält die Zeichen für alle aktiv gesprochenen und viele ältere Sprachen der Welt. Ein UnicodeZeichen wird wie folgt notiert: uAAAA
AAAA steht dabei für die hexadezimale Zahl des Unicode-Zeichens.
Unter Windows ist die Zeichentabelle (START/PROGRAMME/ZUBEHÖR/SYSTEMPROGRAMME/ZEICHENTABELLE) eine gute Anlaufstelle. Sie zeigt für die Zeichen eines Zeichensatzes auch den Unicode an.
17 Unicode steht für Universal Code und vereinheitlicht und erweitert bestehende Zeichensätze.
68
Bezeichner und Typen
Abbildung 2.6: Die Zeichentabelle
1
2 3
4
5
6
7
8
2.3.6 Typkonvertierung JavaScript wechselt Datentypen von Variablen automatisch:
9
var x = "drei"; x = 3;
Zuerst wird Variable x als Datentyp String initialisiert. Anschließend wird ihr ein Integer zugewiesen. Zuerst ist x also ein String, dann eine Zahl.
10
Bei der Typkonvertierung wird durch eine Rechenoperation oder Ähnliches der Datentyp einer Variablen geändert. Im folgenden Beispiel ist die Variable a ein String, die Variable b eine Zahl. Bei der Addition ist der String entscheidend, das heißt, b wird in einen String umgewandelt und beide aneinandergehängt. Die Variable erg erhält also den Wert 12.
11
12
var a = "1"; var b = 2; var erg = a + b;
13
69
Syntax und Variablen
Die automatische Datentypkonvertierung unterscheidet JavaScript von anderen Programmiersprachen wie Java und C#, in denen der Datentyp für eine Variable explizit angegeben werden muss. Sollten Sie die Konvertierung so nicht wünschen, müssen Sie die im nächsten Abschnitt gezeigten Funktionen zur Typkonvertierung einsetzen. Funktionen zur Typkonvertierung Benötigen Sie eine Zahl aus einem String für eine Berechnung oder möchten Sie einen anderen Datentyp in einen String umwandeln, müssen Sie Hilfsfunktionen verwenden. Diese Funktionen sind globale Funktionen, das heißt, sie stehen überall in JavaScript zur Verfügung: ■ ■ ■ ■
REF
Element.toString() wandelt ein Element mit einem anderen Datentyp in einen String um. Number(String) ändert einen String in eine Zahl und entscheidet automatisch, ob das Ergebnis ein Integer oder eine Fließkommazahl ist. parseFloat(String) extrahiert eine Fließkommazahl aus einem String. parseInt(String) wandelt einen String in einen Integer um.
Wie die Funktionen zur Typkonvertierung funktionieren, erfahren Sie in Abschnitt 4.3.2 »Datentypen ändern«. Dort finden Sie auch jeweils ein Beispiel.
2.3.7
Garbage Collection
Variablen reservieren in JavaScript automatisch einen Platz im Hauptspeicher. Wenn sich dann der Wert im Hauptspeicher ändert, müssten C- und C++-Programmierer den alten Wert von Hand löschen. JavaScript erledigt das automatisch. Dieses Prinzip heißt Garbage Collection.18
2.4
Hilfsmittel
Das nächste Kapitel bietet einen tiefer gehenden Einstieg in die Programmierung. Bevor wir damit beginnen, finden Sie hier noch einige nützliche Hilfsmittel, beispielsweise Kommentare, um Skripten mit eigenen Anmerkungen versehen zu können.
2.4.1
Kommentare
Ein Kommentar ist ein Text, der vom JavaScript-Interpreter ignoriert wird. In diesen »ignorierten« Text können Sie also schreiben, was Sie möchten. In der Praxis werden Kommentare verwendet, um den Code zu erklären. Ins18 Für Informatikinteressierte: Die meisten Browser verwenden Garbage-Collection-Varianten des Markand-Sweep-Algorithmus. Er kennzeichnet und entfernt Objekte, auf die von keinem anderen Element verwiesen wird.
70
Hilfsmittel
besondere bei längeren Skripten ist es sehr wichtig, ausführlich zu kommentieren, denn in einem Jahr wissen Sie sicherlich häufig nicht mehr, wie der alte Code genau aufgebaut war. Außerdem helfen Kommentare beim Austausch von Code zwischen verschiedenen Programmierern. JavaScript unterstützt einzeilige und mehrzeilige Kommentare. Einzeilige Kommentare werden mit zwei Schrägstrichen (//) begonnen.
1
var user = "Meier"; //Variable für den Nutzernamen
Ab der Stelle, wo die Kommentarzeichen eingefügt sind, wird der restliche Text oder Code entwertet. So können Sie natürlich auch ganze Codezeilen deaktivieren. Dies ist insbesondere beim Testen und bei der Fehlersuche sinnvoll:
2 3
// document.write(user);
Mehrzeilige Kommentare werden in /* und */ eingeschlossen. Der gesamte Code dazwischen ist inaktiv.
4
var user = "Meier"; /*Variable für der Nutzername wird deklariert*/ var pass = "test";
5
Ein mehrzeiliger Kommentar kann auch einen eigens formatierten Block mit einer Beschreibung, beispielsweise einer Funktion19, enthalten: 6
Listing 2.2: Ein Kommentarblock (kommentar.html) Kommentarblock
7
8
9
10
11
Diese relativ aufwändige Formatierung im Kommentar sorgt zwar für optische Übersichtlichkeit, sollte allerdings erst vorgenommen werden, wenn die Definition der Funktion abgeschlossen und vollständig beschreibbar ist, denn eine nachträgliche Änderung ist je nach Umfang der Dokumentation ein beträchtlicher Mehraufwand.
12
13
19 Siehe hierzu Kapitel 4 »Funktionen«.
71
Syntax und Variablen
2.4.2
Konstanten
Konstanten sind einmal definierte Werte, die sich im Gegensatz zu Variablen nicht ändern und auch nicht ändern sollen. Konstanten werden in JavaScript mit dem Schlüsselwort const eingeleitet: const Name = Wert;
Leider sind Konstanten eine proprietäre Mozilla-Erweiterung und werden deswegen zurzeit nur vom Netscape Navigator ab Version 6 und von Mozilla unterstützt. Tabelle 2.10: const
NS4.x
const
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Für die Namen (Bezeichner) von Konstanten gelten dieselben Regeln wie für Variablennamen. INFO
Im folgenden Beispiel wird der Umrechnungskurs Euro/Dollar in einer Konstanten hinterlegt und dann dazu verwendet, einen Dollarbetrag in Euro umzuwandeln. Listing 2.3: Arbeiten mit einer Konstante (konstante.html) Konstanten
Abbildung 2.7: Der Firefox zeigt die korrekte Umrechnung an.
72
Hilfsmittel
Abbildung 2.8: Der Internet Explorer 7 meldet einen Syntaxfehler, da er const nicht unterstützt.
1
2 3
4
5
6
7
8
9
10
11
12
13
73
Inhalt
3
Programmieren 1
Spricht ein Experte lapidar vom Programmieren, bleibt immer die Frage, worum es dabei eigentlich geht. Eine Webanwendung stellt andere Anforderungen als eine Windows-Applikation oder ein VBA-Makro. Trotz der unterschiedlichsten Einsatzgebiete sind in vielen Programmiersprachen die Grundstrukturen gleich. In C#, Visual Basic, PHP und auch in JavaScript wird mit Variablen gearbeitet. Diese Variablen müssen mittels Operatoren miteinander verknüpft, Bedingungen müssen überprüft und Anweisungen ausgeführt werden.1
2
Sollten Sie bereits mit anderen Programmiersprachen vertraut sein, kommt Ihnen also vieles vertraut vor. Es ist ausreichend, wenn Sie einen Blick auf die Syntax werfen. Haben Sie dagegen noch keine Programmiererfahrung, lohnt sich eine ausführlichere Beschäftigung mit den Grundlagen in diesem Kapitel. Sie können später immer wieder darauf zurückgreifen.
5
3.1
7
3 4
6
Operatoren
Operatoren verbinden in Variablen gespeicherte Daten. Die verschiedenen mathematischen Rechenarten wie Addition, Subtraktion usw. haben jeweils eigene Operatoren. Für die Addition steht beispielsweise das Pluszeichen (+). Darüber hinaus gibt es noch weitere Operatoren, beispielsweise um zwei Werte miteinander zu vergleichen.
8
9
Einen Operator haben Sie bereits kennen gelernt: den Zuweisungsoperator (das Ist-Gleich). Er weist einer Variablen einen Wert zu:
10
var x = 5;
3.1.1
11
Arithmetische Operatoren
Die arithmetischen Operatoren sind für die mathematischen Grundrechenarten, beispielsweise Addition und Multiplikation, zuständig. Die Syntax ist sehr einfach. Folgende Zeile addiert 5 und 3 und weist das Ergebnis 8 der Variablen x zu.
12
13
var x = 5 + 3;
Zugegeben, das ist nur ein kleiner Teil des Programmierens im weiteren Sinne. Für dieses Kapitel wird der Begriff sehr eng gefasst.
75
ndex
1
Programmieren
Der Operator ist in diesem Fall das Plussymbol für die Addition. Die zwei Werte 5 und 3 werden als Operanden2 bezeichnet.
INFO
In den Beispielskripten dieses Buches sind Operanden und Operatoren jeweils durch ein Leerzeichen getrennt. JavaScript erfordert dies nicht, allerdings werden die Skripten dadurch übersichtlicher. Das folgende Beispiel ist ein komplett lauffähiges Skript. Es rechnet einen in der Variablen dollar gespeicherten Geldwert von Dollar in Euro um und gibt ihn aus. Dazu wird die Variable dollar durch den Umrechnungskurs (Variable kurs) geteilt. Listing 3.1:
Einfache Umrechnung von Dollar in Euro (euro.html)
Euro in DM
Beachten Sie, dass der arithmetische Operator für die Division kein Doppelpunkt, sondern ein Schrägstrich (/) ist. HALT
Natürlich können auch mehrere arithmetische Operatoren hintereinander eingesetzt werden. Dabei gilt – wie in der »echten« Mathematik – die Regel »Punkt vor Strich«. Das heißt, Division und Multiplikation rangieren in der Präferenz vor Addition und Subtraktion. Ein einfaches Beispiel illustriert dies: Listing 3.2:
Bei den arithmetischen Operatoren gilt Punkt vor Strich (punkt_vor_strich.html)
Punkt vor Strich 2
76
Frei übersetzt hieße das wohl: »Diejenigen, mit denen der Operator arbeitet«.
Operatoren
Das Beispiel besteht aus zwei Teilen: 1.
2.
Im oberen Teil des Skripts wird die Variable x definiert. Sie erhält als Wert das Ergebnis einer Berechnung. Zuerst wird 200 durch 2 geteilt. Das Ergebnis (100) wird mit 100 addiert. Die Variable x erhält also den Wert 200, der anschließend ausgegeben wird. Der untere Teil des Skripts umgeht das Punkt-vor-Strich-Prinzip mit Klammern. Klammern sind im Prinzip auch Operatoren, die außerdem eine höhere Präferenz als arithmetische Operatoren besitzen. Im Beispiel rechnet der JavaScript-Interpreter zuerst 100 plus 200 und teilt das Ergebnis (300) anschließend durch 2. Ausgegeben wird dann 150 als Wert von y.
1
2
3
Bisher kamen in den Beispielen nur Addition und Division vor. In der folgenden Tabelle finden Sie alle arithmetischen Operatoren im Überblick. Operator
Beispiel
Beschreibung
+
x = 5 + 3; // Ergebnis: 8
Addition; Addieren zweier Zahlen.
-
x = 5 - 3; // Ergebnis: 2
Subtraktion; eine Zahl wird von der anderen subtrahiert.
-
x = 5; y = -x; //Ergebnis y: -5
Negation mit vorangestelltem Minussymbol. Vorzeichenwechsel.
*
x = 5 * 3; // Ergebnis: 15
Multiplikation; zwei Zahlen werden miteinander multipliziert.
/
x = 5 / 3; // Ergebnis: 1.6666 Division; eine Zahl wird mit der zweiten Zahl dividiert.
%
x = 5 % 3; // Ergebnis: 2
Tabelle 3.1: Die arithmetischen Operatoren
4
5
6
7
8
Modulo; ganzzahligen Rest einer Division errechnen; im Beispiel: 3 passt in 5 einmal, als Rest bleibt 2.
9
Inkrement und Dekrement JavaScript hat einen eigenen Operator, das so genannte Inkrement, um einen Wert genau um 1 zu erhöhen. Er wird durch ein doppeltes Pluszeichen (++) symbolisiert und insbesondere bei Schleifen recht häufig zum Erhöhen des Zählers eingesetzt (siehe Abschnitt 3.3 »Schleifen«).
10
11
var x = 2; x++;
Diese zwei Zeilen definieren x zuerst mit dem Wert 2 und erhöhen x dann um 1 auf 3.
12
Das Gegenstück zum Inkrement ist das Dekrement zum Verringern eines Werts um 1. Die Funktionsweise ist analog:
13
var x = 2; x--;
ergibt also den Wert 1 für x. 77
Programmieren
Reihenfolge Bei Inkrement und Dekrement ist entscheidend, ob sie vor oder hinter der Variablen oder dem zu ändernden Wert stehen. ++x;
Stehen Sie wie in dieser Zeile vor der Variablen, wird zuerst der Variablenwert erhöht, bevor die Variable verwendet wird. var x = 5; var a = 3 + ++x;
Bei diesen Zeilen hat also a den Wert 9 ( 3 + 5 + 1). x hat nach den zwei Zeilen den Wert 6. Steht das Inkrement oder Dekrement hingegen hinter der Variablen, wird deren Wert erst um eins erhöht oder gesenkt, wenn die Variable verwendet wurde: var x = 5; var a = 3 + x++;
a hat hier also den Wert 8, x wie im vorhergehenden Beispiel den Wert 6.
Kurzformen Inkrement und Dekrement sind zwar sehr praktisch, häufig soll der Wert einer Variablen allerdings nicht nur um 1 verändert werden. In der umständlicheren Variante sieht dies wie folgt aus: var x = 5; x = x - 3; //Ergebnis: 2
Für die zweite Zeile gibt es allerdings auch eine elegantere Kurzform: x -= 3; //Ergebnis: 2
Was geschieht hier? Der Operator wird vor das Pluszeichen geschrieben. Das signalisiert dem Interpreter, dass er den Variablenwert verändern soll. Um welchen Wert er verändert werden soll, steht nach dem Ist-Gleich. Diese Kurzform gibt es für alle arithmetischen Operatoren (siehe Tabelle 3.2). Tabelle 3.2: Kurzformen für arithmetische Operatoren
78
Operator
Beispiel (var x = 5)
Längere Alternative
+=
X += 3; // Erg: 8
x = x + 3;
-=
X -= 3; // Erg: 2
x = x - 3;
*=
X *= 3; // Erg: 15
x = x * 3;
/=
X /= 3; // Erg: 1.6666
x = x / 3;
%=
X %= 3; // Erg: 2
x = x % 3;
Operatoren
3.1.2
String-Operator
Natürlich lassen sich mit Zeichenketten keine Berechnungen anstellen. Dennoch gibt es einen Operator: das Plussymbol (+). Es verbindet mehrere Strings miteinander. Listing 3.2 macht davon bereits Gebrauch. Die Verknüpfung von Strings wird auch Konkatenation genannt. var titel = "Yesterday"; var text = "all my trouble ..."; document.write(titel + text);
1 INFO
2
Die oberen Zeilen fügen den Anfang des Beatles-Klassikers »Yesterday« aneinander und geben ihn aus. Allerdings gibt es dabei ein Problem. Da keine Leerzeichen als Zwischenräume vorgesehen sind, wird der Text direkt aneinandergehängt (siehe Abbildung 3.1).
3 Abbildung 3.1: Der Browser hängt den Text direkt aneinander.
4
5
6
7 Dieses Problem ist in der Praxis ein sehr häufiger Flüchtigkeitsfehler. Im Prinzip ist es unerheblich, ob Sie das Leerzeichen in den ersten oder zweiten String einfügen. Im Allgemeinen wirkt es allerdings im ersten übersichtlicher. Listing 3.3:
8
Der String-Operator verknüpft zwei Zeichenketten (string_operator.html).
9
String-Operator
10
11
12
13
79
Programmieren
Abbildung 3.2: Jetzt ist der Abstand korrekt.
Kurzform Für den String-Operator gibt es wie bei der normalen Addition die Kurzform +=. Das Beispiel sieht in der Kurzform mit nur noch einer Variablen wie folgt aus: var text = "Yesterday, "; text += "all my trouble…" document.write(text);
3.1.3
Vergleichsoperatoren
Vergleichen ist in der Programmierung gang und gäbe. Es werden keine »Äpfel mit Birnen«, sondern meistens Zahlen miteinander verglichen, um Bedingungen zu überprüfen. Bedingungen kommen insbesondere in Kontrollstrukturen und Schleifen zum Einsatz (siehe Abschnitt 3.2 »Kontrollstrukturen« und Abschnitt 3.3 »Schleifen«). Die Vergleichsoperatoren sind größtenteils ebenfalls bereits im Mathematikunterricht zum Einsatz gekommen. Folgende Zeile stellt beispielsweise fest, ob 4 größer als 3 ist. var v = (4 > 3);
Das Ergebnis eines Vergleichs ist immer ein Wahrheitswert (Boolean), also true (wahr) oder false (falsch). In unserem Beispiel lautet das Ergebnis natürlich … true. Sie können das feststellen, indem Sie die Variable einfach ausgeben (siehe Abbildung 3.3). Abbildung 3.3: Die Ausgabe von 4 > 3
(vergleich.html)
80
Operatoren
Eine vollständige Auflistung der Vergleichsoperatoren in JavaScript finden Sie in Tabelle 3.3. Operator
Beispiel
Beschreibung
==
var a = (4 == 3); //Erg: false
Gleichheit
!=
var a = (4 != 3); //Erg: true
Ungleichheit
<
var a = (4 < 3); //Erg: false
Kleiner als
3); //Erg: true
Größer als
>=
var a = (4 >= 3); //Erg: true
Größer als oder gleich
Tabelle 3.3: Die Vergleichsoperatoren
1
2
3
Sind zwei Zahlen gleich groß, ergibt ein Vergleich mit < oder > immer false. Soll bei Gleichheit true zurückgegeben werden, müssen Sie = verwenden. Beachten Sie außerdem, dass der Vergleich auch Nachkommastellen berücksichtigt. Der Gleichheitsoperator == wird in der Praxis häufig mit dem Zuweisungsoperator = vertauscht. Das Problem ist, dass JavaScript-Interpreter in diesem Fall manchmal keine Fehlermeldung liefern. Die Schwierigkeit liegt also darin, das Problem zu lokalisieren (zur Fehlerkorrektur siehe Kapitel 29 »Debugging«).
4
5
6
HALT
7
Datentypen 8
Ist einer der Operanden keine Zahl, sondern ein anderer Datentyp, führt der JavaScript-Interpreter eine automatische Typkonvertierung durch: ■ ■ ■
Ist einer der Operanden ein String und der andere eine Zahl, wird die Zahl in einen String umgewandelt. Ist einer der Operanden ein Wahrheitswert, wird true in 1 und false in 0 umgewandelt. Ist einer der Operanden ein Objekt, testet der Interpreter, ob das Objekt die Methode toString() (zur Umwandlung in eine Zeichenkette) oder valueOf() (zur Umwandlung in eine Zahl) besitzt. Wenn dies der Fall ist, wird umgewandelt, ansonsten gibt der Browser eine Fehlermeldung aus.
9
10
11
12
Genau gleich (===) und ungleich (!==) Seit JavaScript 1.3 gibt es die Operatoren genau gleich (===)3 und ungleich (!==). Sie überprüfen nicht nur, ob die Werte der zwei Operanden gleich bzw. ungleich sind, sondern auch, ob die Datentypen identisch sind. 3
13
Der Operator genau gleich (===) wird auch Identitätsoperator genannt.
81
Programmieren
Mit der Version 3 wurden die beiden Operatoren in den ECMAScript-Standard übernommen. Die Browserkompatibilität entnehmen Sie Tabelle 3.4. Ältere Browser unterstützen diese Operatoren nicht. Tabelle 3.4: Genau gleich und genau ungleich
HALT
=== !==
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
()
Wenn Sie im Netscape Navigator 4.x als Sprachversion JavaScript 1.2 angeben (language="JavaScript1.2"), werden die Operatoren genau gleich und genau ungleich wie der normale Gleichheits- bzw. Ungleichheitsoperator behandelt. Das vermeiden Sie, indem Sie bei der Sprachangabe die Versionsnummer weglassen. Ein einfaches Beispiel zeigt dies. Das folgende Skript vergleicht eine Zahl mit einer Zeichenkette: ■
Bei der Verwendung des normalen Gleichheitsoperators == wird die Zeichenkette automatisch in eine Zahl konvertiert. Der Vergleich bei der Variablen x ergibt also true. var a = 4; var b = "4"; var x = (a == b);
■
Mit dem Operator genau gleich === wird zusätzlich zum Wert auch der Typ verglichen. Da es sich hier um einen String und eine Zahl handelt, ist das Ergebnis false. var y = (a === b);
Hier der vollständige Code: Listing 3.4:
Der Einsatz von genau gleich (genau_gleich.html)
Vergleichsoperatoren
82
Operatoren
Abbildung 3.4: Der Vergleich mit == liefert true, === ergibt false
1
2 Der Genau-ungleich-Operator ist das Gegenstück zu genau gleich. Er liefert true, wenn zwei Werte ungleich sind und/oder unterschiedliche Datentypen haben.
3
var a = 4; var b = "4"; var x = (a !== b);
4
ergibt also true, obwohl nicht die Werte, sondern nur der Datentyp ungleich ist.
5
Bei einigen Vergleichen von Spezialwerten unterscheiden sich die genauen und die normalen Vergleichsoperatoren ebenfalls: ■ ■
Die Werte null und undefined sind beim Vergleich mit == immer gleich, beim Vergleich mit === dagegen ungleich. Beim Vergleich mit == sind 1 und true bzw. 0 und false gleich, beim Vergleich mit === dagegen nicht.
6 INFO
7
8 Vergleich von Strings Der Vergleich zweier Zeichenketten miteinander ist ein Fall, der bisher noch nicht aufgetreten ist. Zuerst die gute Nachricht: Der Vergleich ist möglich. Und nun die schlechte: Da der ASCII-Code des jeweiligen Zeichens die Grundlage bildet, ist der Vergleich häufig nicht praxistauglich.
9
10
Ein einfaches Beispiel macht den Anfang: var a = "Yesterday"; var b = "Yesterday"; var x = (a == b);
11
Die Variable x hat nun den Wert true, da die beiden Zeichenketten genau identisch sind. Beim Vergleich überprüft der JavaScript-Interpreter jedes Zeichen von links nach rechts. Wenn auch nur ein Zeichen unterschiedlich ist, ergibt der Vergleich sofort false.
12
13
83
Programmieren
Bis jetzt war es noch einfach; bei einem Kleiner-als-Vergleich wird es bereits schwieriger: var a = "a"; var b = "b"; var x = (a < b);
Hier wird der ASCII-Code des kleinen a (95) mit dem des kleinen b (96) verglichen. Da 95 kleiner als 96 ist, ergibt der Vergleich true. Ein weiteres Beispiel, das die Grenzen dieses Vergleichsverfahrens zeigt: var a = "a"; var b = "B"; var x = (a < b);
Das große B befindet sich wie alle Großbuchstaben in der ASCII-Tabelle vor den Kleinbuchstaben. Es hat den ASCII-Code 66. Daraus folgt, dass in diesem Fall der Vergleich false ergibt. Das kleine a ist also größer als das große B. Sind zwei Zeichenketten unterschiedlich lang, vergleicht der Interpreter dennoch von links nach rechts. INFO
var a = "abe"; var b = "Yesterday"; var x = (a < b);
ergibt also false. Sind die Zeichenketten allerdings bei allen vorhandenen Zeichen identisch, ist immer die längere Zeichenkette größer. var a = "Yes"; var b = "Yesterday"; var x = (a < b);
ergibt also true. Unter http://www.asciitable.com/ finden Sie eine übersichtliche ASCIICode-Tabelle. Eine Alternative zu den Vergleichsoperatoren bietet die Methode String.localeCompare(). Sie ist neu in JavaScript 1.5 und deswegen nur in aktuellen Browsern implementiert (siehe Tabelle 3.5).
84
Operatoren
Abbildung 3.5: Eine übersichtliche ASCII-Code-Tabelle
1
2
3 4
5
6
7
8 NS4.x
String. localeCompare()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 3.5: String.localeCompare()
9
10 Die Funktion hat folgende Syntax: String1.localeCompare(String2)
11
Als Ergebnis wird 0 ausgegeben, wenn beide Strings gleich sind, 1, wenn String1 größer ist und –1, wenn String 2 größer ist.4 Für den Vergleich wird der (eingestellte) Zeichensatz des lokalen Betriebssystems verwendet, auf dem der Browser läuft.
12
Folgender Beispielcode zeigt die Anwendung von localeCompare() und ergibt die Bildschirmausgabe x: 1.
13
4
Der Opera liefert bei Ungleichheit nicht 1 und –1, sondern einen größeren positiven oder negativen Wert.
85
Programmieren
Listing 3.5:
String-Vergleich mit localeCompare() (string_compare.html)
String.localeCompare()
Abbildung 3.6: Beachten Sie, dass die Ausgabe von localeCompare() kein Wahrheitswert ist .
3.1.4
Logische Operatoren
Mehrere Vergleiche mit Vergleichsoperatoren müssen in irgendeiner Form miteinander kombiniert werden. Dafür sorgen die logischen Operatoren. Negation (!) Die Negation mit dem Ausrufezeichen kehrt einen Wahrheitswert (Boolean) um. Aus true wird also false und umgekehrt: var x = !true;
x ergibt also den Wert false.
In der Praxis wird dies häufig bei Bedingungsüberprüfungen eingesetzt. Ein einfaches Beispiel: Wenn Sie die Funktion isNaN(Wert) einsetzen, erhalten Sie false, wenn es sich bei dem überprüften Variablenwert (Wert) um eine Zahl handelt. Soll Ihre Bedingung allerdings true liefern, wenn es sich um eine Zahl handelt, müssen Sie den Wahrheitswert mit der Negation umdrehen: var a = "test"; var x = !isNaN(a);
Dieser Code liefert also false.
86
Operatoren
Logisches UND (&&) Das logische UND verknüpft zwei Vergleiche und ergibt nur dann true, wenn beide Vergleiche true liefern. Das zugehörige Symbol wird aus zwei Et-Zeichen (&, auch Ampersand) gebildet. Im folgenden Beispiel verknüpft das logische UND die beiden Vergleiche, ob eine Variable größer 4 und kleiner 6 ist. Der Variablenwert muss also zwischen 4 und 6 liegen, damit die Variable x den Wert true erhält. Listing 3.6:
1
Der Einsatz des logischen UND (logisch.html)
2
Logische Operatoren = 4); document.write("x: " + x); //-->
3 4
5
Die Bildschirmausgabe ist
6
x: true
da die Variable a = 5 zwischen 4 und 6 liegt. 7 Logisches ODER (||) Das logische ODER liefert im Gegensatz zum logischen UND bereits true, wenn nur einer der beiden Vergleiche den Wert true, der andere aber den Wert false hat.
8
Ein einfaches Beispiel illustriert dies. Die Variable x erhält den Wert true, obwohl nur der zweite Vergleich true ergibt, der erste dagegen false:
9
Listing 3.7:
Das logische ODER (logisch_oder.html)
10
Logisches ODER 4); document.write("x: " + x); //-->
11
12
13
87
Programmieren
HALT
Beim Einsatz des logischen ODER kann sich der deutsche Sprachgebrauch als gefährlich erweisen: Das logische ODER liefert auch dann true, wenn beide Vergleiche true liefern. Das »Oder« im Deutschen bezeichnet dagegen normalerweise ein »Entweder … oder ...«. Short-circuit Mit dem Begriff Short-circuit wird ein besonderes Verhalten des JavaScriptInterpreters beschrieben. Wenn der erste Operand beim logischen UND false ist, ergibt die logische Operation zwangsweise false. In diesem Fall überprüft der Interpreter den zweiten Operanden – und damit den zweiten Vergleich – nicht mehr. Dadurch wird Rechenzeit und -performance gespart. var x = (3 > 4 && 5 > 2);
In diesem Beispiel prüft der Interpreter nicht mehr, ob 5 größer 2 ist, sondern bricht vorher ab. Beim logischen ODER steht das Ergebnis true bereits fest, wenn der erste Operand true ergibt. Auch hier wird der zweite Operand nicht geprüft: var x = (3 < 4 || 5 < 2);
Dieses Verhalten ist eigentlich sehr sinnvoll, Sie sollten allerdings bedenken, dass beispielsweise eine Funktion im zweiten Operanden unter Umständen gar nicht mehr aufgerufen wird. Das Short-circuit-Verhalten des Interpreters ist eine leichte Abweichung von der normalen booleschen Algebra. Das logische UND sowie das logische ODER entsprechen also nicht ganz der grundlegenden Definition der booleschen Algebra. In der Praxis ist dieser Unterschied allerdings vernachlässigbar. Enthält der erste Operand keinen Vergleich und auch keinen Wahrheitswert, wird er standardmäßig als true angesehen. HALT
var x = (5 && true);
ergibt also true. Ist der zweite oder sind beide Operanden keine Wahrheitswerte, wird der zweite Operand ausgegeben. Dieses Verhalten ist allerdings nicht – obwohl beobachtbar – festgeschrieben. Daher sollten Sie sich in der Programmierung nicht darauf verlassen. Logische Operatoren mischen Natürlich lassen sich logische Operatoren beliebig miteinander kombinieren. Zusätzlich mit den Vergleichsoperatoren ergeben sich damit alle Möglichkeiten, auch für komplexe Überprüfungen. Eine kleine Quizfrage: Welches Ergebnis liefert der folgende Code?
88
Operatoren
Listing 3.8:
Logische Operatoren mischen (logisch_mischen.html)
Logische Operatoren mischen b) || ((a > c) && (c > b))); document.write("x: " + x); //-->
3
Die lange Zeile mit den logischen Operatoren lässt sich einfach notieren, wenn wir die Ergebnisse der Vergleiche hineinschreiben:
4
1
2
(!true) || (true && true)
Jetzt wird es klarer: Der erste Vergleich liefert true. Dies wird mit der Negation umgekehrt. Der eine Operand des logischen ODER-Vergleichs liefert also false. Der andere muss vom Interpreter noch überprüft werden. Er enthält ein logisches UND, dessen Operanden beide den Wert true haben. Daraus folgt, dass das logische UND true zurückgibt; der zweite Operand des logischen ODER ist also true und damit bleibt auch das Ergebnis, der Wert der Variablen x, true.
5
6
7 Abbildung 3.7: Ein Test im Browser bestätigt das Ergebnis true.
8
9
10
3.1.5
Bitweise Operatoren
11
Die bitweisen Operatoren (auch Bit-Operatoren) dienen dazu, Daten auf BitEbene zu vergleichen und zu verschieben. Ein Computer speichert alle Daten in Bits. Ein Bit kann nur zwei Werte, 0 und 1, annehmen.5
12
In der Praxis werden die bitweisen Operatoren relativ selten eingesetzt. Sollten Sie also wenig Zeit oder Interesse an diesem Thema haben, können Sie den Abschnitt überspringen. Alle Interessierten finden hier zuerst ein klei-
13
5
Da es sich um zwei mögliche Werte handelt, spricht man auch von Binärwert. Die zugehörige Schreibweise für Daten ist die Binärschreibweise.
89
Programmieren
nes Beispiel, anschließend eine Tabelle mit allen verfügbaren Operatoren und zum Schluss eine Umrechnung aus der dezimalen in die binäre Schreibweise. Die binäre Schreibweise besteht aus einem so genannten Muster. Das Muster hat so viele Stellen, wie die Zahl Bits hat. Eine 4-Bit-Zahl hat also vier Stellen und kann 24 Zahlen darstellen. Die bitweisen Operatoren behandeln intern alle Zahlen wie 32 Bit-Werte. 0010
Das obige Bit-Muster steht für die ganze Zahl 2. Ein Bit-Muster liest sich am besten von rechts nach links. Die rechte Zahl steht für die 1, die zweite von rechts für die 2, die dritte für die 4, die vierte für die 8, die fünfte für … raten Sie! Es geht immer in Zweierpotenzen, also ist die nächste 24 = 16. Um die binäre Schreibweise in die dezimale umzurechnen, müssen Sie also immer an der Stelle, an der eine 1 steht, die jeweilige Zahl der Stelle addieren: 1010
ergibt also 8 + 0 + 2 + 0 = 10. Die bitweisen Operatoren wandeln Zahlen intern automatisch in dieses binäre Muster um und bearbeiten es. Das bitweise ODER (|) beispielsweise erstellt ein neues Muster und schreibt an alle Stellen eine 1, an denen in beiden Operanden ebenfalls eine 1 vorkommt: 1010 | 0011
ergibt also 1011. Aus den dezimalen Zahlen 10 (1010) und 3 (0011) wird also 11 (1011). Die übrigen bitweisen Operatoren finden Sie in Tabelle 3.6.
TIPP
Mit parseInt(Binärmuster, 2) können Sie ein Binärmuster sehr einfach in eine Zahl umwandeln. Um eine Zahl in ein Binärmuster zu verwandeln, verwenden Sie toString(2): var a = 15; alert(a.toString(2));
gibt 1111 aus. Beachten Sie, dass die direkte Eingabe von Bit-Mustern in JavaScript nicht möglich ist. Bit-Muster mit beginnender 0 werden vom Interpreter als oktale Schreibweise interpretiert (siehe Abschnitt 2.1.1 im Abschnitt »Oktale Schreibweise«). Bit-Muster mit beginnender 1 dagegen liest er als normale Zahlen. Sie müssen also normale Zahlen nehmen. Der Interpreter wandelt sie dann intern in die binäre Schreibweise um, führt die Operationen durch und gibt eine normale Zahl zurück. Tabelle 3.6 verwendet bei den Beispielen daher nur die Bit-Muster, um die Funktionsweise der Operatoren zu verdeutlichen.
90
Operatoren
Operator
Beispiel
&
1010 & 0011 //Erg: Bitweises UND; schreibt an die Bits eine 1, an denen in beiden Operanden eine 1 vorkommt. 0010 = 2
|
1010 | 0011 //Erg: Bitweises ODER; schreibt an die Stellen eine 1, an denen in einem oder beiden der Operanden 1 vorkommt. 1011 = 11
^
1010 ^ 0011 //Erg: Bitweises ENTWEDER ODER; stellt an die Bits eine 1, an denen nur in einem der beiden Operanden 1 vorkommt. 1001 = 9
~
~1010 //Erg: 0101 =5
Bitweise Negation; ändert alle Bits des Operanden. Aus 0 wird 1 und umgekehrt. Das Tastenkürzel für das Symbol ist (Alt_Gr)+(+).
1010 >> 2 //Erg: 0010 = 2
>>>
1010 >>> 2 //Erg: 0010 = 2
Beschreibung
Tabelle 3.6: Die bitweisen Operatoren
1
2
3 4
Bitweise Verschiebung nach rechts um die vom rechten Operanden angegebenen Positionen (maximal 31). Die Bits, die rechts überstehen, werden gelöscht. Hat der linke Operand ein negatives Vorzeichen, wird links mit Einsen aufgefüllt, ansonsten mit Nullen. Das Verschieben um ein Bit nach rechts entspricht der Division durch 2 (ohne Rest), das um zwei der Division durch 4 usw.
5
6
Bitweise Verschiebung nach rechts. Dabei gehen die Bits, die rechts herausfallen, verloren. Die Bits links werden mit Nullen aufgefüllt.
7
Für alle bitweisen Operatoren gibt es wie bei arithmetischen Operatoren die Kurzform mit integrierter Zuweisung: x dezimal (bis 255) entspricht binär
6
7
8
In Abbildung 3.8 sehen Sie das Ergebnis: eine automatische Umwandlung von dezimalen in binäre Werte. 9 Abbildung 3.8: Der Dezimalwert wird »live« in die binäre Schreibweise umgewandelt.
10
11
12
3.1.6
Operator-Präferenz 13
Bei den arithmetischen Operatoren ist es bereits angeklungen: Operatoren werden vom JavaScript-Interpreter immer in einer festgesetzten Reihenfolge, der Operator-Präferenz, abgearbeitet. Für die arithmetischen Operato-
93
Programmieren
ren entspricht die Reihenfolge der bekannten Punkt-vor-Strich-Regel, Multiplikation und Division haben also eine höhere Präferenz als Addition und Subtraktion. Die Präferenz gibt es allerdings nicht nur für die arithmetischen Operatoren, sondern für alle Operatoren. In Tabelle 3.1 finden Sie die Operatoren in der Präferenz-Reihenfolge von hoch (15) bis niedrig (1) aufgeführt. Einige der Operatoren sind Ihnen noch nicht bekannt. Wir stellen sie in den folgenden Kapiteln vor. INFO
Tabelle 3.7: PräferenzReihenfolge der Operatoren (beginnend bei der höchsten Präferenz 15)
94
Präferenz
Operator
15
. [] () new
14
++ -- (Vorzeichen) ~ ! delete typeof void
13
* / %
12
+ + (Konkatenation)
11
> >>>
10
< >= instanceof
9
== != === !==
8
&
7
^
Kontrollstrukturen
Präferenz
Operator
6
|
5
&&
4
||
3
?:
2
= Kurzformen mit Zuweisung
1
,
Tabelle 3.7: PräferenzReihenfolge der Operatoren (beginnend bei der höchsten Präferenz 15) (Forts.)
1
2
3 3.2
Kontrollstrukturen
In der Programmierung ist es essenziell, verschiedene Wahlmöglichkeiten zu haben. Wie soll das Programm wann handeln? Dies steuern die Kontrollstrukturen, allen voran die if-Anweisung, die – weil grundlegend – bereits in einigen Beispielen vorkam.
3.2.1
4
5
if-Anweisung
6
Die if-Anweisung leitet sich direkt aus dem Sprachgebrauch ab. Ein Blick auf die Syntax verdeutlicht dies: 7
if (Bedingung) { Anweisung1; Anweisung2; }
8
Sie lässt sich einfach vorlesen: »Wenn Bedingung eintritt, führe Anweisungen aus.« Die runden Klammern um die Bedingung und die geschweiften Klammern um die Anweisungen signalisieren dem Interpreter, wann welcher Teil beginnt. Im Sprachgebrauch hat es sich eingebürgert, von der if-Anweisung zu sprechen, wenn das komplette Konstrukt mit Bedingung und darin enthaltener Anweisung gemeint ist. Ist dagegen nur von der Anweisung die Rede, bezieht sich das auf die Anweisungen, die ausgeführt werden, wenn die Bedingung erfüllt ist.
9
10 INFO
11
12
Die Bedingung enthält meist einen Vergleich mit Vergleichsoperatoren, eventuell auch in Verbindung mit logischen Operatoren. Die Anweisung wird nur ausgeführt, wenn die Bedingung true ist. Ansonsten ignoriert der Interpreter die Anweisung und springt an die Stelle hinter der schließenden geschweiften Klammer.
13
95
Programmieren
TIPP
Soll die if-Anweisung ausgeführt werden, wenn die Bedingung false ergibt, drehen Sie das Ergebnis der Überprüfung einfach mit der logischen Negation (!) um. Wie ist das Ergebnis des folgenden Beispiels? Was wird ausgegeben? var groesse = 180; if (groesse > 175) { document.write("Zu groß!"); }
Richtig, der Text »Zu groß!« wird ausgegeben, da die Bedingung der ifAnweisung erfüllt ist. Abbildung 3.9: Der Nutzer ist bereits zu groß für die Sandkiste!
Das vorhergehende Beispiel lässt sich durchaus weiter ausbauen. Was geschieht beispielsweise, wenn die Variable groesse einen geringeren Wert als 175 hat? Um diesen Fall hinzuzufügen, könnten Sie einfach eine weitere if-Anweisung verwenden: if (groesse > 175) { document.write("Zu groß!"); } if (groesse 175 && groesse < 240) { document.write("Zu groß!"); } if (groesse 45) { document.write("Du passt noch ins Auto."); }
96
Kontrollstrukturen
Jetzt gibt es allerdings Fälle, die nicht mehr in die Überprüfungen passen, beispielsweise eine Größe unter 45 oder über 240. Die deckt die else-Anweisung ab. else { document.write("Unrealistisch!"); }
1
Die else-Anweisung wird immer dann ausgeführt, wenn die vorherigen ifAnweisungen nicht zutreffen. Sie ist gewissermaßen das Auffangbecken für alle Fälle, die vorher nicht berücksichtigt werden. Die Syntax sieht wie folgt aus:
2
if (Bedingung) { Anweisung; } else { Anweisung; }
3 4
else if Mehrere if-Anweisungen hintereinander werden immer komplett durchgeprüft. Trifft die Bedingung jeweils zu, wird die Anweisung ausgeführt. Dieses Verhalten ist häufig erwünscht, manchmal aber auch unpraktisch.
5
Ein Beispiel: Sie betreiben eine Website mit Mitgliedern, die unterschiedliche Berechtigungsstufen von 1 bis 6 haben. Die 6 Berechtigungsstufen sollen in drei Nutzerklassen eingeteilt werden: Luxusnutzer (5 und 6), Mittelklasse (3 und 4) sowie Einsteigerpakete (1 und 2), die in der Praxis beispielsweise auf drei verschiedene Seiten verlinken könnten.
6
7
Betrachten Sie die folgende Fallunterscheidung. Erreicht sie das gewünschte Ziel und trennt die drei Klassen?
8
var z = 5; if (z >= 5) { document.write("Luxusnutzer"); } if (z >= 3) { document.write("Mittelklasse"); } if (z >= 1) { document.write("Einsteigerpaket"); }
9
10
11
Ein Blick in den Browser zeigt das Problem (siehe Abbildung 3.10). Wenn die Variable z gleich 5 oder 6 ist, treffen alle drei if-Bedingungen zu und der Nutzer wird in jede Klasse eingeordnet.
12
13
97
Programmieren
Abbildung 3.10: Der Nutzer wird allen drei Kategorien gleichzeitig zugeordnet.
Um dieses Problem zu umgehen, verwenden Sie das else if-Konstrukt. Es hat folgende Syntax: if (Bedingung) { Anweisung; } else if (Bedingung) { Anweisung; }
Die else if-Bedingung wird nur geprüft, wenn die if-Bedingung nicht zutrifft. Im Beispiel ist es ausreichend, die zwei letzten if-Anweisungen durch else if zu ersetzen: Listing 3.10: Die else if-Anweisung (else_if.html) else if-Anweisung = 5) { document.write("Luxusnutzer"); } else if (z >= 3) { document.write("Mittelklasse"); } else if (z >= 1) { document.write("Einsteigerpaket"); } //-->
Abbildung 3.11: Nun funktioniert die Unterscheidung
98
Kontrollstrukturen
Das hier beschriebene Problem ist natürlich auch auf anderem Wege lösbar. Beispielsweise ließen sich die Bedingungen der if-Anweisungen so anpassen, dass es nicht mehr zu Überschneidungen kommt:
INFO
if (z == 3 || z == 4) {
else if ist allerdings ein schneller und einfacher Weg.
1
Ursprünge von else if In vielen JavaScript-Büchern und Referenzen taucht else if nicht auf. Dies ist – zumindest unter Gesichtspunkten der vollständigen Sprachabdeckung – kein Versäumnis, da else if ein Konstrukt ist, das aus if- und else-Anweisungen gebildet wird.
2
3
Die else if-Anweisung besteht aus einer else-Anweisung, bei der einfach die geschweiften Klammern weggelassen wurden (siehe nächster Abschnitt »Kurzformen«). Die else-Anweisung erhält als Anweisung ein if. Verständlich wird dies, wenn man sich die Langform zur else if-Syntax anschaut:
4
if (Bedingung) { Anweisung; } else { if (Bedingung) { Anweisung; } }
5
6
Mehrere else if-Anweisungen hintereinander sind also lediglich ineinander verschachtelte else- und if-Anweisungen. Unser Beispiel sieht in der Langform wie folgt aus:
7
Listing 3.11: Die lange Version von else if (else_if_lang.html)
8
else if-Anweisung = 5) { document.write("Luxusnutzer"); } else { if (z >= 3) { document.write("Mittelklasse"); } else { if (z >= 1) { document.write("Einsteigerpaket"); } } }
9
10
11
12
13
99
Programmieren
//-->
Achten Sie insbesondere auf die Einrückungen. Funktional gibt es keine Unterschiede, die else if-Konstruktion ist allerdings wesentlich übersichtlicher als die Langform. Kurzformen Die else if-Konstruktion zeigt bereits eine Kurzform der if-Anweisung. Sie können die geschweiften Klammern weglassen. Normalerweise allerdings nur, wenn die Anweisung lediglich aus einer Zeile besteht. Hat sie mehrere Zeilen, nennt man das auch Anweisungsblock. Ein solcher Block muss in geschweiften Klammern stehen. if (a < 3) document.write(a);
ist also erlaubt. if (a < 3) document.write(a); document.write(++a);
dagegen wirkt sich anders aus. Hier gibt der JavaScript-Interpreter ++a auf jeden Fall aus, auch wenn a größer 3 ist, da die zweite Anweisung nicht mehr zur if-Anweisung gehört, obwohl die Einrückung das hier anzudeuten scheint. Für übersichtlichen Code sollten Sie immer geschweifte Klammern verwenden. Eine Ausnahme ist natürlich die else if-Konstruktion. TIPP
Eine komplette if-Anweisung kann außerdem in nur einer Zeile stehen. if (a
2
Wenn Sie das Skript testen, wird der richtige Wochentag angezeigt (siehe Abbildung 3.12).
3 Abbildung 3.12: Der richtige Wochentag wird ausgegeben.
4
5
6
Wenn Sie break aus Versehen weglassen, werden alle Anweisungen ab dem zutreffenden Fall ausgeführt. In Abbildung 3.13 ist tag gleich 3, also »Donnerstag«. Da break fehlt, werden allerdings auch »Freitag« bis »Sonntag« ausgegeben.
7 HALT
8 Abbildung 3.13: Ohne break werden alle Anweisungen ab der erfüllten Bedingung ausgeführt.
9
10
11
12
Typkonvertierung Im Gegensatz zum Einsatz von Vergleichsoperatoren wird in der switchcase-Anweisung keine automatische Typkonvertierung durchgeführt. Folgender Code ergibt also keine Ausgabe, da der Wert der Variablen ein String ist, der zugehörige Fall aber eine Zahl:
13
103
Programmieren
var tag = "3"; switch (tag) { case 0: document.write("Montag"); break; case 1: document.write("Dienstag"); break; case 2: document.write("Mittwoch"); break; case 3: document.write("Donnerstag"); break; case 4: document.write("Freitag"); break; case 5: document.write("Samstag"); break; case 6: document.write("Sonntag"); break; }
default Wenn wie bei der Verwendung eines falschen Datentyps aus dem vorigen Abschnitt keiner der Fälle eintrifft, ist es oft ärgerlich, dass der Nutzer keine Rückmeldung erhält. Um dieses Problem zu beheben, fügen Sie einfach am Ende eine default-Anweisung an. Sie tritt ein, wenn vorher keiner der Fälle denselben Wert wie der Ausdruck hat. Die Syntax der switch-Anweisung wird um die default-Anweisung erweitert und damit ein wenig länger: switch (Ausdruck) { case Wert1: Anweisung1; break; case Wert2: Anweisung2; break; default: Anweisung; }
Wird das Beispiel mit den Wochentagen um eine default-Anweisung erweitert, kann damit auch eine Falscheingabe, beispielsweise der Wert 7 für den Wochentag, abgefangen werden:
104
Kontrollstrukturen
Listing 3.13: Der Einsatz von default (switch_default.html, nicht vollständig abgedruckt) var tag = 7; switch (tag) { case 0: document.write("Montag"); break; case 1: document.write("Dienstag"); break; case 2: document.write("Mittwoch"); break;
1
2
. . .
3 default: document.write("Keiner der Fälle ist eingetreten");
4
}
Abbildung 3.14: Keiner der sieben Wochentage konnte identifiziert werden.
5
6
7 switch und if im Vergleich
8
Wenn nur mögliche Werte eines Ausdrucks nacheinander ausgewertet werden sollen, wird eine if-Anweisung unnötig kompliziert. Dies zeigt der direkte Vergleich zwischen switch und if:
9
Listing 3.14: Das switch-Beispiel, mit if realisiert (if_switch.html) if oder switch
Zwar ist die if-else if-Variante um einige Zeilen8 kürzer, dafür besteht sie aus einigen Zeichen weniger und fordert damit auch mehr fehlerträchtige Tipparbeit. Außerdem wirkt die switch-Variante optisch aufgeräumter. Schließlich muss jeder selbst abwägen, was ihm besser gefällt. Viele Programmierer greifen automatisch zu if-else if, da sie dieses Konstrukt besser kennen. Behalten Sie aber die Alternative switch im Hinterkopf, wenn es an neue Aufgaben geht.
3.3
Schleifen
Schleifen enthalten stetig sich wiederholende Anweisungen. Sie werden so lange ausgeführt, bis eine vorher festgelegte Bedingung nicht mehr erfüllt ist. Fehlt diese Bedingung oder ist sie immer erfüllt, läuft die Schleife ewig. Man spricht dann von einer Endlosschleife. Endlosschleifen gehören zu den Fehlern, die es unbedingt zu vermeiden gilt, denn häufig stürzt dem Nutzer sonst der Browser ab.
TIPP
Haben Sie beim Programmieren eine Endlosschleife erzeugt, klicken Sie im Browser auf das Stoppsymbol oder betätigen Sie (Esc), um den JavaScriptInterpreter anzuhalten. Manche Browser fragen im Falle einer Endlosschleife auch nach, ob sie das Skript abbrechen sollen (siehe Abbildung 3.15).
Abbildung 3.15: Der Browser erkundigt sich, ob das Skript weiter ausgeführt werden soll.
3.3.1
for
Die for-Schleife ist die komfortabelste und daher auch meistverbreitete Schleifenart. Wie oft die Schleife durchlaufen wird, steuern drei Parameter: 8
106
Um genau zu sein: sieben Zeilen. Natürlich könnten Sie weitere Zeilen einsparen, indem Sie else ifAnweisungen auf eine Zeile komprimieren. Ähnlich lassen sich auch switch-Fälle in eine Zeile schreiben. Der Nachteil bei beiden Methoden ist, dass die Übersichtlichkeit sehr stark leidet.
Schleifen
for (Initialisierung; Bedingung; Änderung) { Anweisungen; }
Die drei Parameter dienen alle zur Steuerung des Schleifenzählers: ■
■ ■
Bei der Initialisierung wird typischerweise einer Zählervariablen ein erster Wert vergeben. Diese Anweisung wird nur einmal, zu Beginn, ausgeführt. Die Bedingung wird vor jedem Schleifendurchlauf überprüft. Ist sie erfüllt (true), werden die Anweisungen in der Schleife ausgeführt. Nach jedem Schleifendurchlauf wird in der Änderung die Zählervariable angepasst. Dies geschieht so lange, bis die Zählervariable die Bedingung nicht mehr erfüllt und die Schleife abgebrochen wird. Natürlich kann die Zählervariable auch in der Schleife geändert werden und in der Änderung folgen andere Anpassungen.
1
2
3 4
Jeder der drei Schleifenparameter kann weggelassen werden, nicht aber die abtrennenden Strichpunkte. Fehlt die Bedingung, ist sie automatisch immer true. In diesem Fall wird aus der for-Schleife eine Endlosschleife, wenn sie nicht in den Anweisungen mit break abgebrochen wird.
INFO
5
Ein einfaches Beispiel gibt die Quadrate der Zahlen von 1 bis 10 aus: ■
6
Dazu wird der Zähler auf 1 initialisiert. for (var i=1;
7 Die Zählervariable muss nicht unbedingt erst in der for-Schleife deklariert werden. Meist wird allerdings diese Technik verwendet, da die Zählervariable nur in der Schleife eingesetzt wird. Hier ist allerdings Vorsicht geboten: Selbst wenn die Zählervariable erst in der for-Schleife deklariert wird, steht sie auch nach der Schleife noch zur Verfügung. Sie ist also keine lokale Variable, wie es sie bei Funktionen gibt (siehe Kapitel 4 »Funktionen«). ■
INFO
8
9
In der Bedingung überprüft die for-Schleife, ob der Zähler kleiner oder gleich 10 ist.
10
for (var i=1; i
Abbildung 3.16: Die for-Schleife gibt die Quadrate der Zahlen von 1 bis 10 aus.
Komma-Operator (,) Der Komma-Operator ist der Operator mit der niedrigsten Präferenz (siehe Abschnitt 3.1.6 »Operator-Präferenz«). Er dient dazu, gleichwertige Anweisungen voneinander zu trennen. In einer for-Schleife lassen sich für jeden der drei Parameter mehrere, durch Kommata getrennte, Anweisungen vergeben. Im folgenden Beispiel nutzen wir dies, um eine zweite Zählervariable t einzusetzen, die gegenläufig zur ersten von 10 nach unten gezählt wird. Die beiden Variablen werden in der Anweisung als Produkt i * t ausgegeben: Listing 3.16: Eine for-Schleife mit mehreren Anweisungen für jeden Parameter (for_komma.html) for-Schleife = 1; i++, t--) { document.write(i*t + ""); } //-->
108
Schleifen
Mit dieser Schleife erzeugen Sie eine symmetrische Zahlenfolge (1*10, 2*9, 3*8 …, siehe Abbildung 3.17). Abbildung 3.17: Die Schleife ergibt eine symmetrische Zahlenfolge.
1
2
3 4 break 5
Die break-Anweisung kennen Sie schon von der switch-Fallunterscheidung (siehe Abschnitt 3.2.2 »switch case«). Sie lässt sich auch dazu verwenden, forSchleifen abzubrechen.
6
Der folgende Code würde die Zahlen von 1 bis 100 untereinander ausgeben. Im Schleifenkörper ist allerdings eine if-Überprüfung eingebaut. Sie wird aktiv, wenn die Zählervariable gleich 12 ist, und bricht dann die komplette Schleife mit break ab:
7
Listing 3.17: Eine for-Schleife mit break abbrechen (for_break.html)
8
for und break
9
10
11
12
break können Sie auch bei den anderen Schleifentypen wie while- und do while-Schleifen einsetzen.
13 TIPP
109
Programmieren
Abbildung 3.18: Bei 12 ist dank break Schluss, obwohl der Schleifenzähler bis 100 reicht.
continue Der Befehl continue ist das Gegenstück zu break. Er springt zum nächsten Schleifendurchlauf. Allerdings werden für den aktuellen Durchlauf alle Anweisungen ignoriert, die nach continue folgen. Das folgende Beispiel nutzt continue, um bei den Zahlen von 1 bis 10 den Bereich von 4 bis 6 wegzulassen. Mit einer if-Anweisung wird überprüft, ob die Zählervariable in diesem Durchlauf zwischen 4 und 6 liegt. Ist dies der Fall, wird der Schleifendurchgang mit continue abgebrochen und gleich der nächste Durchgang gestartet. Dadurch wird die Ausgabeanweisung für die Zählervariable übersprungen. Listing 3.18: continue in einer for-Schleife (for_continue.html) for und continue 3 && i < 7) { continue; } document.write(i + ""); } //-->
continue funktioniert bei allen Schleifentypen, auch while- und do whileSchleifen. TIPP
110
Schleifen
Abbildung 3.19: Die Zahlen 4 bis 6 werden ausgespart.
1
2
3
Verschachtelte Schleifen Natürlich können Sie Schleifen beliebig ineinander verschachteln. Folgende Schleifenkombination gibt beispielsweise fünfmal hintereinander die Quadrate der Zahlen von 1 bis 10 aus:
4
Listing 3.19: Ausschnitt von verschachtelten for-Schleifen (for_verschachtelt.html)
5
for (var i=1; i
2 Abbildung 3.21: Die Schleife wird nur einmal ausgeführt.
3 4
5
6
7 In JavaScript lassen sich nicht nur Schleifen, sondern auch beliebige andere Anweisungen beschriften. Bei Schleifen ist der Einsatz von Namen allerdings am üblichsten und in der Praxis sinnvoll.
8
for-in
9
Die for-in-Schleife ist eine Unterart der for-Schleife mit einem ganz bestimmten Zweck. Sie soll Elemente aus Objekten und Arrays auslesen. 10 Da sowohl Objekte als auch Arrays in den Kapiteln 6 und 7 ausführlich behandelt werden, finden Sie hier nur die Syntax und ein kurzes Beispiel zur for-in-Schleife.
REF
11
Die Besonderheit der for-in-Schleife ist der in-Operator.9 Ein Blick auf die Syntax verdeutlicht die Funktionsweise:
12
for (Variable in Objekt) { Anweisung }
13
9
Es handelt sich hier tatsächlich um einen Operator. Allerdings ist dies keine für die Praxis wichtige Unterscheidung.
113
Programmieren
Die Variable nimmt bei jedem Schleifendurchlauf eine Eigenschaft des Objekts auf. Beendet wird die Schleife, wenn alle Eigenschaften eines Objekts durchlaufen sind. Anhand eines Arrays wird das verständlicher. Ein Array besteht aus beliebig vielen Daten, die standardmäßig mit einem ganzzahligen Index versehen werden, der bei 0 beginnt: ■
Im folgenden Array hat »München« den Index 0, »Frankfurt« den Index 1 und »Berlin« den Index 2. var a = new Array("München", "Frankfurt", "Berlin");
■
Eine for-in-Schleife liest das Array aus: for (var index in a) {
■
In der Variablen index wird die Indexnummer der verschiedenen Array-Elemente gespeichert. Im ersten Schleifendurchlauf ist das 0, im zweiten 1, im dritten 2. Ein vierter Durchlauf erfolgt nicht, da das Array nur drei Elemente hat. Die Schleife wird beendet. Im Schleifenkörper können Sie jetzt mit dem Index arbeiten. Die folgende Zeile gibt zuerst den Index aus und anschließend das zugehörige Element (mit dem Array-Namen und dem Index in eckigen Klammern). document.write(index + ": " + a[index] + "");
Hier das vollständige Skript: Listing 3.21: for-in (for_in.html) for in
Abbildung 3.22: Der Inhalt des Arrays wird ausgegeben.
114
Schleifen
3.3.2
while
Die while-Schleife ist wesentlich einfacher aufgebaut als die for-Schleife. Einziger Parameter ist die Bedingung, die vor jedem Schleifendurchlauf überprüft wird. Tritt sie ein, werden die Anweisungen im Schleifenkörper ausgeführt: while (Bedingung) { Anweisungen; }
1
Dennoch können Sie, wie der folgende Code beweist, mit while dieselben Aufgaben wie mit einer for-Schleife bewältigen:
2
Listing 3.22: Die while-Schleife kann genauso viel wie for (while.html).
3
while
4
5
6
7 Abbildung 3.23: Mit der whileSchleife lassen sich dieselben Aufgaben erledigen wie mit for.
8
9
10
11
12
13
115
Programmieren
Dieser Code gibt die Quadrate der Zahlen von 1 bis 10 aus (siehe analog Listing 3.15). Gegenüber der for-Schleife muss die Zählervariable allerdings bereits außerhalb der Schleife definiert und im Schleifenkörper bei jedem Durchlauf hochgezählt werden. Die Syntax für eine while-Schleife mit Zähler sieht also wie folgt aus: Zählerinitialisierung while (Bedingung) { Anweisungen; Zähleränderung }
Wann ist while sinnvoller? Welche Schleife Sie zu welchem Zweck einsetzen, ist im Prinzip Geschmacksache. Erfolgt die Überprüfung der Bedingung nicht direkt in den Parametern der Schleife, sondern in einer if-Anweisung, wird oft die whileSchleife bevorzugt. Folgendes Beispiel arbeitet mit einer while-Schleife, die erst abgebrochen wird, wenn die if-Bedingung erfüllt und eine durch 21 teilbare Zahl unter 100 gefunden wurde: Listing 3.23: Ist while manchmal besser als for? (while_besser.html) while besser als for?
INFO
Auch dieses Beispiel lässt sich natürlich mit einer for-Schleife realisieren. Hier ist die while-Schleife ohne die zwei zusätzlichen Parameter jedoch praktischer, da in einer for-Schleife die Bedingung nicht mit den Zählervariablen übereinstimmen würde: for (var zahl = 100; weiter == true; zahl--) {
ist für das Verständnis nicht sehr sinnvoll.
116
Schleifen
Die while-Schleife im letzten Beispiel ließe sich auch noch einfacher gestalten, indem die Bedingung auf true gesetzt und in der if-Anweisung break verwendet würde: while (true) { if (zahl % 21 == 0) { document.write(zahl + " ist durch 21 teilbar"); break; } zahl--; }
1
2
Dies funktioniert zwar identisch, balanciert allerdings noch näher an der Endlosschleife, da die Bedingung nie false werden kann.
3 Abbildung 3.24: Die erste durch 21 teilbare Zahl wurde gefunden und ausgegeben.
4
5
6
3.3.3
7
do while
Die do while-Schleife überprüft die Bedingung erst am Ende: do { Anweisungen; } while (Bedingung)
8
Das heißt auch, dass die Anweisungen im Schleifenkörper mindestens einmal ausgeführt werden, unabhängig davon, ob die Bedingung am Anfang erfüllt oder nicht erfüllt ist.
9
10
do while gehört noch nicht seit den Anfängen zu JavaScript wie die anderen Schleifen. Netscape 2 und 3 und Internet Explorer 3 in beiden Varianten unterstützen sie nicht. Aus heutiger Sicht werden allerdings alle aktuellen und wichtigen Browser unterstützt. NS4.x
NS6
NS7
IE4
IE5
IE5.5
IE6
O7
M1
K3
11 Tabelle 3.9: do while-Schleife
12
13
117
Programmieren
Ein Beispiel verdeutlicht die Besonderheit der do while-Schleife: Listing 3.24: Die do while-Schleife (do_while.html) do while-Schleife
Die Bedingung i
Abbildung 4.1: Die Funktion gibt Text aus.
Sehr häufig werden Funktionen in Event-Handlern aufgerufen. Event-Handler stehen als Attribute in HTML-Tags: Listing 4.2:
Eine Funktion im Event-Handler onclick aufrufen (funktion_eventhandler.html)
Funktion und Event-Handler
120
Eigene Funktionen
Mehr Informationen zu Event-Handlern erhalten Sie in Kapitel 9 »Ereignisse und Event-Handler«. Die Anweisung alert("Text") gibt ein JavaScriptWarnfenster aus. Mehr hierzu in Kapitel 17 »Fenster«. 1 Abbildung 4.2: Die Ausgabe beim Anklicken der Schaltfläche
2
3
4 5
6
7
4.1.1
Parameter
Bisher sind die runden Klammern hinter dem Funktionsnamen immer leer geblieben. Dort kann der Funktion beim Aufruf allerdings ein oder – durch Kommata getrennt – mehrere Parameter übergeben werden. Die Parameter werden dann als normale Variablen in den Funktionsanweisungen eingesetzt.
8
Eine einfache Funktionsdeklaration mit zwei Parametern hat folgende Syntax:
10
function Name(Parameter1, Parameter2) { Anweisungen; }
11
9
Beim Aufruf können für die beiden Parameter Werte angegeben werden: 12
Name(Parameterwert1, Parameterwert2);
Das folgende Beispiel verwendet einen Parameter. Die Funktion berechnet das Quadrat einer Zahl, die als Parameter übergeben wird, und gibt das Ergebnis aus:
13
121
Funktionen
Listing 4.3:
Eine Funktion mit Parameter aufrufen (funktion_parameter.html)
Funktion mit Parameter
Abbildung 4.3: Die Ausgabe
TIPP
Sollten Sie beim Funktionsaufruf vergessen, für einen der Parameter einen Wert anzugeben, erhält er den Wert undefined. Übergeben Sie mehr Werte, als Parameter in der Funktion definiert sind, werden die ersten Werte den Parametern der Reihe nach zugeordnet. Auf zusätzliche Werte können Sie mit Funktionsname.arguments[Index]
zugreifen, wobei alle Parameter einen Index, beginnend bei 0, besitzen.
4.1.2
Rückgabewert (return)
Häufig soll eine Funktion an den Funktionsaufruf ein Ergebnis liefern. Dieser Rückgabewert kann beispielsweise in einer Variablen gespeichert sein und wird mit dem Schlüsselwort return übergeben. function Name(Parameter1, Parameter2) { Anweisungen; return Ergebnis; }
TIPP
122
return darf nur innerhalb von Funktionen verwendet werden. Außerdem beendet die Ausführung von return eine Funktion sofort. Alle danach folgenden Anweisungen werden ignoriert.
Eigene Funktionen
Das folgende Beispiel illustriert die Arbeit mit return: ■
Der Funktionsaufruf erfolgt innerhalb einer document.write()-Anweisung. document.write(quadrat(12));
■
■
In der Funktion wird das Quadrat des Parameters z ausgerechnet. function quadrat(z) { var y = z * z;
1
Das Schlüsselwort return gibt das Ergebnis – gespeichert in der Variablen y – zurück. Es kann dann von document.write() ausgegeben werden.
2
return y; }
3
Hier der vollständige Code: Listing 4.4: Die Funktion gibt einen Wert zurück (funktion_rueckgabe.html).
4
Funktion mit Rückgabe
5
6
7
8 Abbildung 4.4: Die Funktion liefert an den Funktionsaufruf das Quadrat von 12 zurück, das erst dann ausgegeben wird.
9
10
11
12
Sollte bei return kein Ergebnis zurückgeliefert werden, ist der Rückgabewert undefined. Folgender Code gibt also undefined aus: function quadrat(z) { var y = z * z; return; } document.write(quadrat(12));
13
123
Funktionen
Abbildung 4.5: Wird kein Ergebnis mitgeliefert, ist der Rückgabewert undefined, da die Multiplikation scheitert. Damit kann der Nutzer natürlich nicht viel anfangen …
4.1.3
Funktionen abbrechen
Wie bereits erwähnt, bricht eine return-Rückgabe die Funktion sofort ab. Im folgenden Beispiel wird eine Funktion mit der Rückgabe einer Fehlermeldung abgebrochen, wenn das Quadrat einer Zahl den Wert 100 übersteigt: Listing 4.5:
return bricht eine Funktion ab (funktion_abbrechen.html)
Eine Funktion abbrechen 100) { return "Zu groß"; } document.write(z + " * " + z + " = "); return y; } document.write(quadrat(11)); //-->
Da in unserem Beispiel das Quadrat von 11 größer als 100 ist, wird der Text »Zu groß« zurückgegeben. Die nach der if-Fallunterscheidung folgenden Anweisungen werden nicht mehr ausgeführt (siehe Abbildung 4.6). Die break-Anweisung, die bei der switch-Fallunterscheidung und den verschiedenen Schleifen zum Abbrechen dient, funktioniert bei Funktionen nicht!
HALT
124
Wird return in einer Fallunterscheidung eingesetzt, sollten Sie für bessere Abwärtskompatibilität nicht nur eine if-, sondern auch eine else-Anweisung mit return definieren. Ansonsten liefert der Netscape Navigator 4.0x eine Fehlermeldung, weil der Code nicht immer einen Rückgabewert liefert.
Eigene Funktionen
Abbildung 4.6: Anweisungen nach return werden ignoriert.
1
2
4.1.4
Funktionsaufrufe verschachteln
3
Ein Funktionsaufruf kann als Parameter durchaus einen anderen Funktionsaufruf haben. Dieser wird zuerst ausgeführt und der zurückgegebene Wert ist dann der Parameter für den ersten Funktionsaufruf.
4
Das folgende Beispiel ruft eine Funktion produkt auf und übergibt für die zwei Parameter zwei weitere Aufrufe der Funktion produkt.
5
Listing 4.6: Einen Funktionsaufruf verschachteln (aufruf_verschachteln.html) Funktionsaufruf verschachteln
6
7
8
9 Abbildung 4.7: Der verschachtelte Funktionsaufruf liefert das Produkt aus (3 * 4) * (2 * 5), also 120.
10
11
12
13
125
Funktionen
4.1.5
Funktionen verschachteln
Eine Funktionsdefinition kann durchaus einen Funktionsaufruf oder eine andere Funktionsdefinition enthalten. Funktionsaufrufe in Funktionen Funktionsaufrufe in Funktionen sind allgemein üblich, da der Code damit praktisch aus mehreren Funktionen zusammengesetzt werden kann. Das folgende Beispiel illustriert dies: ■
In der document.write()-Ausgabe wird die Funktion berechne() mit drei Parametern aufgerufen. document.write(berechne(5, 3, 6));
■
Diese Funktion zieht das Quadrat des ersten Parameters vom Produkt der nächsten zwei Parameter ab. Für die Berechnung von Quadrat und Produkt werden allerdings wieder gleichnamige Funktionen verwendet. function berechne(x, t, z) { return quadrat(x) - produkt(t, z); }
Abbildung 4.8: Die Berechnung mit drei Funktionen ergibt schlicht und einfach 7.
Hier das komplette Beispiel: Listing 4.7:
Funktionsaufrufe in einer Funktion (funktion_in_funktion.html).
Funktionsaufruf in Funktion
Funktionsdefinitionen in Funktionen
1
Funktionsdefinitionen in anderen Funktionen sind in der Praxis wesentlich seltener als Funktionsaufrufe in Funktionen. Dies hat mehrere Gründe: ■ ■
2
Definitionen in Funktionen machen den Code unübersichtlicher. Die innerhalb von Funktionen definierten Funktionen sind nicht im gesamten Skript verfügbar, sondern nur in der Funktion, in der sie definiert wurden.
3
Obwohl sie relativ selten vorkommen, haben Funktionsdefinitionen in Funktionen jedoch ein Beispiel verdient. Listing 4.7 lässt sich einfach umschreiben, sodass die zwei Hilfsfunktionen quadrat() und produkt() innerhalb der Funktion berechne() definiert werden:
4
Listing 4.8: Die zwei Hilfsfunktionen sind jetzt in der Funktion berechne() deklariert (Ausschnitt aus:
5
definition_in_funktion.html). function berechne(x, t, z) { function quadrat(x) { return x * x; } function produkt(t, z) { return t * z; } return quadrat(x) - produkt(t, z); } document.write(berechne(5, 3, 6));
6
7
8
Wenn Sie das Skript testen, erhalten Sie dasselbe Ergebnis wie im letzten Abschnitt, allerdings mit den vorher aufgezählten Nachteilen. Funktionsdefinitionen können zwar ineinander verschachtelt sein, dürfen aber nur auf oberster Ebene in anderen Funktionen stehen. Funktionsdeklarationen in Funktionen in anderen Anweisungen wie Fallunterscheidungen und Schleifen sind verboten.
9
10 HALT
11
4.1.6
Lokale und globale Variablen 12
Bei der Arbeit mit Funktionen spielt der Gültigkeitsbereich von Variablen eine große Rolle. In JavaScript gibt es zwei verschiedene Gültigkeitsbereiche: ■
13
Lokale Variablen sind Variablen, die innerhalb einer Funktion definiert wurden. Sie sind nur in der Funktion gültig und damit auch nur dort verwendbar.
127
Funktionen
■
Globale Variablen werden außerhalb von Funktionen deklariert. Sie sind überall gültig. Das heißt, jede Anweisung kann auf den Wert einer globalen Variablen zugreifen. Globale Variablen sind auch innerhalb aller Funktionen auf einer Seite gültig.
Im folgenden Beispiel wird zuerst eine globale Variable g definiert. Auf g kann jede Anweisung zugreifen. Daher können Sie g innerhalb und außerhalb der Funktion ausgabe() mit document.write() auf den Bildschirm bringen. Die Variable l dagegen ist lokal und bleibt auf die Funktion ausgabe() beschränkt. Listing 4.9: Globale und lokale Variablen (global_lokal.html) Gültigkeitsbereich von Variablen
Abbildung 4.9: Die lokale und die globale Variable werden in der Funktion ausgegeben; außerhalb nur die globale.
Innerhalb der Funktion werden die lokale – dort definierte – Variable l und die globale Variable g ausgegeben, außerhalb nur die globale (siehe Abbildung 4.9). Der Browser (in Abbildung 4.10 der Internet Explorer 6) liefert eine Fehlermeldung, dass die Variable l nicht definiert ist. Verständlich, denn sie ist ja nur innerhalb der Funktion ausgabe() gültig. 128
Eigene Funktionen
Heißt eine lokale Variable in der Funktion genauso wie eine globale Variable, steht die globale Variable in der Funktion ab der Stelle nicht mehr zur Verfügung, wo die lokale Variable mit var deklariert wurde. Außerhalb der Funktion bleibt die globale Variable allerdings unverändert. Dies gilt übrigens auch für Parameternamen, die wie globale Variablen heißen.
HALT
Abbildung 4.10: Der Internet Explorer meldet bei nicht deaktivierten Meldungsfenstern, dass die Variable l nicht definiert ist.
1
2
3
4 5
6
Ob eine Variable in einem -Bereich im oder im Bereich einer Webseite deklariert wird, hat auf ihren Gültigkeitsbereich keine Auswirkung. Er wird nur davon bestimmt, ob die Variable innerhalb oder außerhalb einer Funktion definiert wurde.
4.1.7
7 INFO
8
Rekursion
9
Als Rekursion wird der Vorgang bezeichnet, wenn sich eine Funktion selbst aufruft. Programmierer verwenden die Rekursion im Allgemeinen, um bestimmte Anweisungen öfter auszuführen.
10
In vielen Fällen führen Schleifen schneller ans Ziel, aber manchmal ist die Rekursion ein durchaus mächtiges Mittel.
11 INFO
Das folgende Beispiel nimmt eine beliebige Zahl und addiert alle geraden Zahlen, die kleiner oder gleich dieser Zahl sind. Ist die Zahl 7, werden 6, 4 und 2 addiert. Das Ergebnis ist 12. Um diese Aufgabe zu bewältigen, wird in einer Fallunterscheidung festgestellt, ob die Zahl z bereits gleich 0 ist. Eine else if-Anweisung überprüft mit einem bitweisen Operator (siehe Kapitel 3 »Programmieren«), ob es sich um eine ungerade Zahl handelt. Wenn dies der Fall ist, wird sie um 1 verringert und die Funktion damit neu aufgerufen.
12
13
129
Funktionen
Nach dieser ersten Rekursion – nur bei einer ungeraden Zahl – gibt die elseAnweisung die Summe aus der Zahl und dem rekursiven Aufruf der Funktion mit der um 2 verringerten Zahl zurück. Diese Rekursion sorgt dafür, dass alle geraden Zahlen addiert werden. Listing 4.10: Arbeiten mit Rekursion (funktion_rekursion.html) Rekursion
Abbildung 4.11: Die Summe aller geraden Zahlen kleiner als 7 ergibt 12.
4.1.8
this
Soll eine Funktion in einem Event-Handler aufgerufen und mit einem HTML-Element verknüpft werden, muss sie das aufrufende HTML-Element kennen. Statt nun umständlich mit name-Attributen zu arbeiten, verwenden Sie als Parameter für die Funktion einfach das Schlüsselwort this. Es liefert in diesem Kontext das HTML-Element. Der Parameter kann innerhalb der Funktion als Variable verwendet werden.
130
Eigene Funktionen
this wird in diesem Buch noch öfter auftauchen. Vor allem bei der Arbeit mit Formular- und anderen HTML-Elementen und in der objektorientierten Programmierung ist es sehr gebräuchlich.
INFO
Ein kleines Beispiel soll bereits an dieser Stelle zeigen, welche Möglichkeiten this bietet. Die Webseite besteht aus einem Textfeld. Sobald der Nutzer etwas ändert, wird der neue Text in einem Warnfenster ausgegeben (siehe Abbildung 4.12). So funktioniert es im Einzelnen: ■
1
Im Event-Handler onchange, der eintritt, wenn etwas im Textfeld geändert wird, wird eine Funktion mit this als Parameter aufgerufen.
2
■
3
this liefert an die Variable element in der Funktion den Zugriff auf das Textfeld. function ausgabe(element) {
■
4
Im Warnfenster wird der Wert des Textfelds ausgegeben. alert("Text im Feld: " + element.value);
Abbildung 4.12: Der Text aus dem Formularfeld wird in einem Warnfenster ausgegeben.
5
6
7
8
9 Hier der komplette Code: 10
Listing 4.11: Der Einsatz von this (this.html) this
11
12
13
131
Funktionen
4.2
Funktionen als Operator
Funktionen können auch als Wert für eine Variable definiert werden. Man spricht dann von einem so genannten Funktionsliteral. Das Schlüsselwort function bildet einen Operator. Diese Möglichkeit wird bei modernen clientseitigen Frameworks und in Ajax-Applikationen häufig eingesetzt (siehe Tabelle 4.2). Tabelle 4.2: Funktionen als Operator
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Nachfolgend die Syntax: var Name = function(Parameter1, Parameter2) { Anweisungen; }
Bei dieser Art der Funktionsdefinition wird der Funktionsname weggelassen. Der Aufruf erfolgt wie bei normalen Funktionen auch. Als Funktionsname wird der Name der Variablen verwendet: Name(Parameter1, Parameter2);
So lässt sich das bisher verwendete einfache Beispiel mit einer Funktion, die einen Parameter quadriert zurückgibt, sehr einfach auch mit einem Funktionsliteral umsetzen: Listing 4.12: Eine Funktion als Operator (funktion_operator.html) Funktion als Operator
Abbildung 4.13: Die Funktion als Operator liefert das richtige Ergebnis.
132
Globale Funktionen
Funktionen als Operator dürfen wie normale Funktionen verschachtelt werden. Sie können sogar in Fallunterscheidungen und Schleifen eingebunden werden. Funktionen lassen sich auch als JavaScript-Objekt definieren und verwenden. Mehr dazu erfahren Sie in Kapitel 6 »Objekte in JavaScript«. 1
TIPP
4.3
Globale Funktionen 2
JavaScript besitzt einige Funktionen, die Sie in Ihren Skripten jederzeit und überall nutzen können. Daher heißen diese Funktionen auch globale Funktionen. Diese Funktionen haben verschiedenste Anwendungsgebiete. Im Folgenden finden Sie einen gesammelten Überblick, einzelne Funktionen begegnen Ihnen in anderen Kapiteln wieder. Die meisten Möglichkeiten liefert JavaScript allerdings in Objekten. Zur Arbeit mit Objekten lesen Sie das nächste Kapitel. Später wird in fast allen Kapiteln mit Objekten programmiert.
4.3.1
3
4 INFO
5
Zahlentests 6
Die zwei Funktionen isFinite(Zahl) und isNaN(Wert) kennen Sie bereits: ■
isFinite(Zahl) liefert true, wenn die als Parameter übergebene Zahl nicht größer oder kleiner als die maximal in JavaScript darstellbare Zahl ist.
7
Listing 4.13: Test auf Unendlichkeit (infinite.html)
8
isFinite ■
9
10
11
12
isNaN(Wert) gibt true zurück, wenn es sich bei einem Wert nicht um eine Zahl, sondern einen anderen Datentyp handelt.
13
133
Funktionen
Abbildung 4.14: Irgendwann ist für JavaScript bei Zahlen Schluss.
Listing 4.14: Test, ob eine Variable eine Zahl ist (Ausschnitt aus: isNaN.html) var x = "text"; if (isNaN(x)) { document.write("x ist keine Zahl"); }
Abbildung 4.15: Die Variable x ist keine Zahl.
Unendlichkeit testen Wer hat sich nicht schon einmal dafür interessiert, wann die Unendlichkeit eintritt? Vermutlich jeder, der sich nicht mit Philosophie oder Mathematik beschäftigt. Es kann jedoch ganz interessant sein, festzustellen, wo JavaScript seine Unendlichkeitsgrenze hat. Folgendes Skript erledigt das mit einer einfachen Schleife: Listing 4.15: Testen, wo die Unendlichkeit in JavaScript liegt (unendlich_test.html) Unendlichkeitstest
Abbildung 4.16: Die Zahl in der zweiten Zeile lässt sich in JavaScript noch anzeigen.
1
2
3
4 4.3.2
Datentypen ändern
JavaScript konvertiert Datentypen zwar automatisch (siehe Abschnitt 2.3.6 »Typkonvertierung«), manchmal ist es allerdings dennoch nötig, Hand anzulegen.
5
6 In String konvertieren Die Funktionen toString() oder String(Ausdruck) dienen dazu, einen beliebigen Datentyp in einen String zu konvertieren. Sie stehen für sämtliche JavaScript- und DOM-Objekte zur Verfügung (mehr zu Objekten erfahren Sie in Kapitel 6 »Objekte in JavaScript«).
7
8
var x = 3; document.write(x.toString() + 3);
9
Listing 4.16: Mit toString() in eine Zeichenkette konvertieren (Ausschnitt aus: tostring.html) Abbildung 4.17: Die Zahlen werden nicht addiert, sondern hintereinander ausgegeben, da das Skript die erste Zahl in einen String umgewandelt hat.
toString()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
10
11
12
13
Tabelle 4.3: toString()
135
Funktionen
toString() ist auch in Uraltbrowsern ab Netscape 2 und Internet Explorer 3 verfügbar. toString() hat bei verschiedenen Datentypen unterschiedliche Auswirkungen: ■ ■ ■ ■ ■
Strings bleiben Strings. Booleans (Wahrheitswerte) werden als »true« oder »false« ausgegeben. Zahlen werden zur Basis 10 direkt in Strings umgewandelt. Bei Arrays werden die einzelnen Array-Elemente durch Kommata getrennt ausgegeben. Bei Objekten können nur manche einen sinnvollen String liefern, beispielsweise wenn sie eine Eigenschaft haben.
Als optionaler Parameter für toString(Basis) können Sie die Basis angeben, wenn Sie eine Zahl umwandeln:1 ■ ■
10 ist das Dezimalsystem. 2 steht für die binäre Schreibweise. Folgender Code gibt also 11 aus, das Binärmuster für 3. var x = 3; document.write(x.toString(2));
■
16 ist die Basis für die hexadezimale Schreibweise. Dieser Code gibt also ff aus. var x = 255; document.write(x.toString(16));
■
8 bildet die Basis für die oktale Schreibweise. Folgender Code gibt für 24 den oktalen Wert 30 aus. var x = 24; document.write(x.toString(8));
INFO
Informationen zu den unterschiedlichen Schreibweisen erhalten Sie in Abschnitt 2.1.1 »Integer« und für die binäre Schreibweise in Abschnitt 3.1.5 »Bitweise Operatoren«. In Zahlen konvertieren Um Strings in Zahlen zu konvertieren, gibt es drei mögliche Funktionen: Number(), parseInt() und parseFloat(): ■
Number(String) wandelt einen String entweder in ein Integer oder eine Gleitkommazahl um, je nachdem, ob sich im String ein Dezimalpunkt befindet. Handelt es sich bei dem String nicht um eine umwandelbare Zahl, liefert die Funktion NaN zurück. Folgende Zeilen geben also 8.4 aus. var x = "5.4"; document.write(Number(x) + 3);
1
136
Netscape 2 und Internet Explorer 3 in der ersten Version können dies nicht.
Globale Funktionen
Bei älteren Browsern, Netscape 2 und 3 und Internet Explorer 3 in beiden Varianten, funktioniert Number() noch nicht. Neuere Browser werden allerdings alle unterstützt. Die anderen zwei Konvertierungsfunktionen stehen auch für die genannten älteren Browser zur Verfügung.
■
NS4.x
NS6
NS7
IE4
IE5
IE5.5
IE6
O7
M1
K3
1
2
parseFloat(String) wandelt die Zeichenkette automatisch in eine Fließkommazahl um.
Beachten Sie, dass im Deutschen Fließkommazahlen mit Komma geschrieben werden. Die korrekte Umwandlung funktioniert aber nur, wenn statt des Kommas ein Punkt angegeben wird. ■
Tabelle 4.4: Konvertierungsfunktionen
3 HALT
4
parseInt(String) wandelt den String in ein Integer um. Eventuell vorhandene Nachkommastellen werden weggelassen. Wie bei toString(String, Basis) lässt sich auch hier die Basis angeben (parseInt(String, Basis) (Werte siehe toString()).
5
6
Beim Konvertieren von Nutzerangaben sollten Sie als Basis immer 10 angeben, um Fehleingaben zu vermeiden. TIPP
7
Sie können die hier beschriebenen Funktionen auch verwenden, um eine Zahl aus Text herauszuschneiden. Die Funktionen wandeln die Zahl um, die am Anfang der Zeichenkette stehen muss. Der komplette nachfolgende Text wird abgeschnitten.
8
Folgender Code schneidet beispielsweise die Währungsangabe Euro hinter den Beträgen ab, rechnet sie zusammen und gibt das Ergebnis wieder mit der Währungsangabe aus:
9
Listing 4.17: Währung abschneiden und anfügen (parseFloat_abschneiden.html)
10
Mit parseFloat() Text abschneiden
11
12
13
137
Funktionen
Abbildung 4.18: Das Ergebnis ist wieder mit der Währungsangabe versehen.
HALT
Vorsicht ist allerdings angebracht, wenn Nachkommastellen mit Komma angegeben sind. Diese ignoriert parseFloat(), da die Funktion nur Dezimalpunkte kennt.
4.3.3
eval()
Die Funktion eval(String) ist recht mächtig. Sie wertet den als Parameter übergebenen String als JavaScript-Code aus. Das klingt noch nicht so spannend? JavaScript-Code wird prinzipiell in drei Bereichen ausgeführt. Im allgemeinen Code zwischen den -Tags, in Event-Attributen wie z.B. onclick von HTML-Objekten und als eval()-Anweisung. Wenn der JavaScript-Interpreter ein Skript bearbeitet, führt er die Anweisungen zwischen den -Tags sofort aus. Funktionen werden »vorkompiliert«, das heißt, ihre Anweisungen werden im Hauptspeicher abgelegt und mit dem Funktionsnamen verknüpft, falls er aufgerufen wird. Die eval()-Anweisung dagegen wird beim Aufruf ausgeführt. Da bei dieser Lösung jedes Mal aufwändig der Text analysiert werden muss, wird insbesondere bei größeren Anwendungen häufig auf sie verzichtet. Folgendes Beispiel erlaubt dem Nutzer die Eingabe von JavaScript-Code in ein Textfeld. Klickt er anschließend auf die Schaltfläche, wird die Funktion ausgabe() aufgerufen. Sie führt den in das Textfeld eingegebenen Code mit eval() als JavaScript-Code aus. Listing 4.18: Mit eval() ein Eingabeformular für Code programmieren (eval.html) eval() Code-Eingabe
138
Globale Funktionen
Abbildung 4.19: Der Nutzer gibt JavaScript-Code in das Textfeld ein. Beim Anklicken der Schaltfläche wird der Code ausgewertet und das Ergebnis ausgegeben.
1
2
3
4 5
6
7 Wird in eval() ein Objekt ausgewertet, gibt eval() eine Verknüpfung auf das Objekt zurück. eval("window");
8
ist also eine Verknüpfung auf das window-Objekt. Folgender Code gibt alle Eigenschaften des window-Objekts zurück:
9
Listing 4.19: Objektverknüpfung mit eval() (eval_rueckgabe.html) eval() mit Rückgabe
10
11
12
13
139
Funktionen
Abbildung 4.20: Eigenschaften des window-Objekts
INFO
Eine Alternative zu eval() ist die Methode execScript(String, Sprache). Sie ist eine Methode des window-Objekts (siehe Kapitel 6 »Objekte in JavaScript«) und kann Code in verschiedenen Sprachen ausführen. Mögliche Parameter für die Sprache sind JavaScript, JScript und VBScript. Insbesondere wenn Sie Code in einer anderen Sprache, beispielsweise VBScript, ausführen möchten, ist diese Methode hilfreich. Im Gegensatz zu eval() liefert sie nichts zurück. Allerdings bleibt sie auf den Internet Explorer beschränkt.
4.3.4 URLs Eine URL (Uniform Resource Locator) in der Adressleiste des Browsers wird nicht in normaler Schreibweise dargestellt. Leerzeichen entsprechen beispielsweise dem Hexadezimalcode %20. Daher müssen Sie, wenn Sie in JavaScript mit URLs arbeiten, diese erst umwandeln.
140
Globale Funktionen
Die Schreibweise für URLs heißt URI-Kodierung (Encoding). URI steht für Uniform Resource Identifier. INFO
Die Funktion escape(String) wandelt eine normale Zeichenkette in URIKodierung um. Allerdings entspricht ihre Konvertierung nicht vollständig den Regeln für die URL selbst. Sie sollte insofern nur für die Werte der URLParameter eingesetzt werden. Hier ein Beispiel für die Wirkung:
1
Listing 4.20: escape() (escape.html)
2
escape()
3
4 5 Abbildung 4.21: Die URL wurde umgewandelt.
6
7
8
escape(String) kodiert alle alphanumerischen Zeichen außer dem Plussymbol (+), da dies als Symbol für URLs bei Suchanfragen dient. Insofern wird auch der Doppelpunkt nach dem http konvertiert, ein Effekt, der für die URL nicht gewünscht ist.
9 TIPP
10
unescape(URL) sorgt für die Rückumwandlung eines kodierten Strings in eine Zeichenkette.
11
Listing 4.21: unescape() (Ausschnitt aus: unescape.html)
12
var x = unescape("http%3A//www.mut.de/Ajax%20JS%20Buch/"); document.write(x);
13
141
Funktionen
Abbildung 4.22: Die URL ist wieder ein normaler String.
Neue Funktionen In neueren Browsern gibt es vier neue Funktionen zum Kodieren und Dekodieren von URLs. Der Grund für die neuen Funktionen war, dass escape()außer dem Pluszeichen (+) alle alphanumerischen Zeichen kodiert. In der URI2 sind auch einige andere Zeichen wie beispielsweise das Fragezeichen (?) zum Abtrennen von Daten bei der Übertragung mit GET in serverseitigen Programmiersprachen erlaubt. Die neuen Funktionen wandeln diese Zeichen nicht um. Tabelle 4.5: encodeURI(), decodeURI(), encodeURIComponent() und decodeURIComponent()
Tabelle 4.6: Von encodeURI() nicht umgewandelte Zeichen
NS4.x
M/FF
IE4
IE5
a.
IE5.5
IE6
IE7
Op
SF/KQ
a
Erst ab Version 1.2 des Safari bzw. 3.3 des Konqueror.
Das direkte Pendant zu escape() bildet encodeURI(). Es arbeitet wie escape(), wandelt allerdings folgende Zeichen nicht um: ?
;
/
:
@
&
=
+
$
,
-
_
.
!
~
*
'
(
)
#
Für die Rückumwandlung wird statt unescape() einfach decodeURI() eingesetzt. Im folgenden Beispiel wird ein String in eine URL umgewandelt, ausgegeben, rückumgewandelt und wieder ausgegeben: Listing 4.22: encodeURI() und decodeURI() im Einsatz (encodeURI_decodeURI.html) encodeURI() und decodeURI()
1
Abbildung 4.23: Kodiert und dekodiert
2
3
4 5
Die zwei Funktionen encodeURIComponent(String) und decodeURIComponent(String) sind Varianten von encodeURI(String) und decodeURI (String). Sie dienen dazu, nur einzelne Teile einer URL zu kodieren. Daher kodieren sie auch die von encodeURI() ausgelassenen Zeichen (auch das Pluszeichen (+)) und sollten auch nicht für komplette URLs eingesetzt werden.
6
7
Das vorhergehende Beispiel sieht für encodeURIComponent(String) und decodeURIComponent(String) wie folgt aus:
8
Listing 4.23: Stärkere Kodierung (components.html)
9
var x = encodeURIComponent("http://www.mut.de/JS Buch/test.html?autor=hauser"); document.write(x + ""); var x = decodeURIComponent(x); document.write(x);
10
In Abbildung 4.24 sehen Sie, dass alle alphanumerischen Zeichen in der URL kodiert werden.
11 Abbildung 4.24: encodeURIComponent() kodiert die URL wesentlich stärker.
12
13
143
Inhalt
5
OOP 1
2
Als zur Laufzeit interpretierte Sprache besitzt JavaScript einige Besonderheiten: die Typisierung, sprich die Zuordnung von Datentypen, erfolgt erst zur Laufzeit und auch die Vererbung in der Objektorientierung geschieht erst zur Laufzeit. Andere Sprachen definieren den Datentyp und auch Angaben zur Objektorientierung mit Schlüsselwörtern direkt im Quellcode. Daraus ergibt sich in JavaScript ein Ansatz, der von den bekannten objektorientierten Sprachen wie Java und C# abweicht. In der Praxis lässt sich mit JavaScript sehr gut objektorientiert programmieren, wie Sie in diesem Kapitel sehen werden. Beispielsweise bietet JavaScript statt der klassenbasierten Vererbung die prototypbasierte.
3
4
5
Natürlich können Sie mit JavaScript auch rein prozedurale – also ablauforientierte bzw. strukturierte – Skripten schreiben. Insbesondere bei den kürzeren Skripten im Web ist das sogar wesentlich häufiger anzutreffen. Prozedural bedeutet, dass das Skript nach einem bestimmten Schema abläuft: Fallunterscheidungen und Schleifen bestimmen den Ablauf und mit Funktionen können häufige Anweisungen zentral verwaltet werden.
6
7
Aber auch wenn Sie prozedural programmieren, verwenden Sie Objekte. Zwar programmieren Sie keine eigenen, aber viele (nahezu alle) Möglichkeiten von JavaScript werden in Objekten zur Verfügung gestellt. Man unterscheidet hier in Objekte, die JavaScript selbst zur Verfügung stellt: dazu gehört z.B. das Math-Objekt, und auf der anderen Seite Objekte, die der Browser zur Verfügung stellt, wie document. Denken Sie nur an die Anweisung document.write() zur Ausgabe. document ist eines der wichtigsten Browserobjekte, write() eine Methode dieses Objekts.
8
9
10
Im folgenden Abschnitt erhalten Sie eine Antwort auf die Frage, was Objekte eigentlich sind und was mit Objektorientierung in JavaScript gemeint ist. Anschließend folgt eine ausführlichere Einführung in Objektorientierung mit JavaScript. Sollten Sie in der Objektorientierung in einer anderen Sprache bereits fit sein, ist das Kapitel dennoch wichtig, da es doch erhebliche Unterschiede zu anderen Sprachen gibt. Im nächsten Kapitel werden die wichtigsten Objekte von JavaScript kurz vorgestellt. Einigen Objekten, beispielsweise Date, Math und String, ist dort mehr Raum gewidmet, andere – vor allem die Objekte zur Interaktion mit dem Browser – finden Sie in vielen Kapiteln der folgenden Buchteile ausführlich mit Beispielen illustriert.
11
12
145
ndex
13
OOP
5.1
Was sind Objekte?
Die Objektorientierung scheint erst einmal ein sehr abstraktes Thema zu sein. Das ist – salopp ausgedrückt – eigentlich Unsinn. Insbesondere im Konkreten, Realen, leicht Vorstellbaren liegt die Stärke der Objektorientierung. Stellen Sie sich ein reales Objekt vor, beispielsweise einen Vogel. Einen Vogel komplett in die Programmierung umzusetzen ist schwierig, eigentlich sogar unmöglich. Selbst ein 3D-Bild kann beispielsweise sein Verhalten nicht einfangen. In der Objektorientierung bleibt die Betrachtung abstrakter. Einige Eigenschaften legen fest, um welchen Vogel es sich handelt. Die Art legt beispielsweise seine Rasse fest, die Eigenschaft Laut legt fest, welche Laute ein Vogel von sich geben kann. Damit der Vogel auch etwas tut, werden für ihn Methoden programmiert. Beispielsweise die Methode Fressen, damit er Körner picken kann. Die Methode Laut geben könnte die Eigenschaft Laut ausgeben. Abbildung 5.1: Ein Vogel kann mit einfachen Mitteln – hier zwei Eigenschaften und zwei Methoden – bereits einiges anfangen.
Führt man die vier soeben beschriebenen Eigenschaften und Methoden in einem einfachen Diagramm zusammen, ergibt sich bereits eine Struktur, wie das Objekt Vogel in der Programmierung aussehen könnte (siehe Abbildung 5.1). Diese Struktur nennt man in der objektorientierten Programmierung Klasse. Die Klasse Vogel bildet also das Schema für alle Objekte. Jeder einzelne Vogel hat die Struktur der übergeordneten Klasse. Ein Vogel namens Kra hat beispielsweise die Art Papagei und gibt den Laut Kra von sich. Ein anderer Vogel heißt dagegen beispielsweise Wuschl, hat die Art Meise und den Laut Tschiep (siehe Abbildung 5.2).
INFO
146
Kein Objekt ist gleich, auch wenn es dieselben Eigenschaften und dieselbe Objektbezeichnung hat. Dieses Prinzip heißt Objektidentität. Es ist sinnvoll, da es einen Papagei Kra sowohl in einem Käfig in Los Angeles als auch in einem Zoo in Tokio geben könnte.
Was sind Objekte?
Abbildung 5.2: So können Objekte und ihre Klasse notiert werden.
1
2
3
Ein Objekt ist eine Instanz einer Klasse. Das Erzeugen eines Objekts heißt auch instanziieren. Kra ist also beispielsweise eine Instanz der Klasse Vogel. In JavaScript wird ein Objekt mit dem Schlüsselwort new instanziiert.
4
5
var x = new String("Text");
Diese Zeile instanziiert beispielsweise ein String-Objekt. Bei eigenen Objekten müssen Sie in JavaScript vorher selbst die Struktur anlegen. Das StringObjekt ist dagegen in JavaScript bereits als Klasse vorhanden. Die bereits in JavaScript vorhandenen Klassen werden meist als Objekte bezeichnet. Der Kontext verrät allerdings, ob es sich um die Instanz oder um das übergeordnete Objekt (die Klasse) handelt.
6
7 INFO
8
5.1.1
Eigenschaften
Wenn Sie ein neues String-Objekt instanziieren, erhält es automatisch alle Eigenschaften und Methoden der Klasse String – wie auch ein Objekt von der Klasse Vogel alle Eigenschaften und Methoden erhält (siehe Abbildung 5.2). Eine Eigenschaft ist ein dem Objekt zugeordneter Wert.
9
10
Auf Eigenschaften zugreifen Um auf die Eigenschaft eines Objekts zuzugreifen, schreiben Sie den Objektnamen (auch Objektbezeichner) und nach einem Punkt1 die Eigenschaft:
11
document.write(x.length);
12
gibt die Länge des String-Objekts x aus. Da es als Zeichenkette »Text« enthält, gibt der Interpreter 4 Zeichen als Länge aus (siehe Abbildung 5.3).
13 1
Der Punkt ist ein Operator. Der Operand links vom Punkt ist eine Referenz auf das Objekt, der Operand rechts die Eigenschaft oder Methode.
147
OOP
Abbildung 5.3: Das String-Objekt hat eine Länge von 4 Zeichen.
Eigenschaften ändern Die meisten Eigenschaften von vorgegebenen Objekten können nur gelesen werden. Einige Eigenschaften lassen sich allerdings auch ändern. Ein Beispiel ist das location-Objekt. Es hat eine Eigenschaft href, die die URL (Uniform Resource Locator) der aktuellen Seite enthält. Wenn Sie diese Eigenschaft setzen, leitet der Browser auf die neue URL um: window.location.href="http://www.mut.de";
Dieser Code leitet also auf die Homepage von Markt+Technik um.
INFO
Vor location.href ist das Objekt window notiert. Der Grund ist, dass location ein Unterobjekt von window ist. Mehr dazu erfahren Sie in Abschnitt 5.1.3 »Vererbung«.
5.1.2
Methoden
Methoden eines Objekts führen etwas aus. Sie sind das, was ein Objekt kann. Daher haben Methoden Ähnlichkeiten mit Funktionen. Allerdings sind Methoden immer an ein Objekt gebunden. Das String-Objekt hat beispielsweise sehr viele Methoden. Eine davon, die Methode big(), legt automatisch die HTML-Tags und um den Text, damit er größer hervorgehoben wird. var x = new String("Text"); document.write(x.big());
Abbildung 5.4: Der Text wird mit der Methode big() größer dargestellt.
148
Was sind Objekte?
Parameter übergeben Methoden werden in JavaScript wie Funktionen mit runden Klammern abgeschlossen. Eine weitere Parallele zu Funktionen ist, dass in den runden Klammern Parameter übergeben werden dürfen. Die Methode link(URL) des String-Objekts versieht beispielsweise einen String mit einem Link auf eine Webadresse, die als Parameter angegeben sein muss: Listing 5.1:
1
Eine Methode mit Parameter (methode_parameter.html)
Methode mit Parameter
2
3
4
Abbildung 5.5: Der funktionierende Link wurde mit einer Methode des String-Objekts erzeugt.
5 6
7
8
5.1.3
Vererbung
9
Das Konzept der Vererbung ist eine der Stärken der Objektorientierung. Eine Klasse kann von einer anderen Klasse ihre Eigenschaften und Methoden erben. Sie ist dann eine Unterklasse.
10
Abbildung 5.6 zeigt das Prinzip: Die Art des Vogels ist hier keine Eigenschaft mehr, sondern eine eigene Klasse. Dadurch können bestimmte Vogelrassen andere Eigenschaften und Methoden haben als andere. Der Papagei kann beispielsweise auch sprechen, die Meise nicht.
11
12
Ein instanziiertes Objekt der Klasse Papagei, hier also Kra, übernimmt die Methode Sprechen aus der Struktur seiner Klasse Papagei. Die Eigenschaften und Methoden Laut, Laut geben und Fressen übernimmt er von der übergeordneten Klasse Vogel.
13
149
OOP
Abbildung 5.6: Das Prinzip der Vererbung
Sie haben die Vererbung bereits bei einem in JavaScript integrierten Objekt2 gesehen. Das Objekt location ist ein Unterobjekt von window. Das windowObjekt ist noch viel mehr: Es ist das übergeordnete Objekt für alle Objekte, die in einem Browserfenster und damit auf einer Webseite vorkommen. Für eigene Objekte bietet JavaScript die prototypbasierte Vererbung. Mehr dazu finden Sie im entsprechenden Abschnitt »Vererbung«. REF
5.1.4
Polymorphismus
Wenn eine Methode in zwei Klassen verschiedene Funktionen hat, nennt man dies Polymorphismus. Ein Beispiel ist der Aufruf der Methode braten. Ein Objekt der Klasse Mensch macht dann den Herd warm. Ein Objekt der Klasse Vogel sollte dagegen schleunigst das Weite suchen.
5.2
Eigene Objekte
Für Objekte bzw. Klassen gibt es in JavaScript kein eigenes Schlüsselwort. Stattdessen wird meist function verwendet. function Objektname() { Eigenschaften und Methoden } 2
150
Hier ist mit Objekt nicht das instanziierte Objekt, sondern die Klasse gemeint.
Eigene Objekte
Der Objektname folgt wie ein Funktionsname nach function. Die runden Klammern sind erforderlich. In den geschweiften Klammern stehen die Eigenschaften und Methoden, die wir in den folgenden Abschnitten behandeln. Wollen Sie das Objekt verwenden, müssen Sie es instanziieren. Dazu dient der bereits bekannte Konstruktor new:
1
var Objektinstanz = new Objektname();
Diese Zeile erzeugt also eine Instanz des Objekts mit dem Namen Objektname.
2
Eigene Objekte sind in dieser Form auch in alten Browsern wie Netscape ab Version 2 und Internet Explorer 3 möglich.
5.2.1
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
3 Tabelle 5.1: Eigene Objekte
4
5
Eigenschaften
Eigene Objekte erhalten ihre Eigenschaften direkt im function-Block: 6
function Objektname() { this.Eigenschaft1 = Wert; this.Eigenschaft2 = Wert; }
7
Das Schlüsselwort this besagt, dass die Eigenschaft genau für dieses Objekt definiert wird. Beachten Sie, dass Sie ohne dieses Schlüsselwort auf die Eigenschaft nicht zugreifen können!
8
Der Zugriff auf eine Eigenschaft erfolgt über den Objektinstanznamen, gefolgt von einem Punkt und dann dem Namen der Eigenschaft. 9
var Ojektinstanz = new Objektname(); Objektinstanz.Eigenschaft1
Ein kleines Beispiel zeigt dies. Das Objekt Wuschl wird mit der Eigenschaft Laut definiert. Anschließend erhält das Objekt eine Instanz, deren Eigenschaft Laut dann abgefragt wird.
10
Eine Objekteigenschaft definieren und abfragen (eigenschaft_eigene.html)
11
Listing 5.2:
Eigenschaft
Sie kennen das Objekt Wuschl bereits aus Abschnitt 5.1 »Was sind Objekte?«. Wuschl ist eine Meise. Die Klasse Vogel definieren wir an dieser Stelle nicht. REF
Abbildung 5.7: Wuschl gibt »Tschiep« als Laut zurück.
TIPP
Der Objektinstanz das Kürzel obj voranzustellen und dann den Objektnamen folgen zu lassen, ist gängige Praxis, aber natürlich nicht verpflichtend. Aber dennoch sollten Sie eine Namenskonvention wählen, die erkennen lässt, dass es sich um eine Instanz handelt und von welchem Objekt sie abstammt.
5.2.2
Methoden
Eine Methode in einem eigenen Objekt ist im Prinzip eine Funktion. Das heißt, die Methode wird wieder mit dem function-Schlüsselwort definiert.3 function Methode() { Anweisungen }
Im Objekt muss die Methode verankert sein. Dazu wird sie – wie eine Eigenschaft – mit ihrem Namen definiert: function Objektname() { this.Methodenname = Methode; }
Wichtig ist auch hier das Schlüsselwort this, damit die Methode dem Objekt zugewiesen wird, in dem sie steht. Die Methode wird später mit dem Methodennamen aufgerufen. Der Methodenname für den function-Aufruf (in der Syntax Methode genannt) erscheint außerhalb des Objekts nicht. Allerdings wird in der Praxis häufig derselbe Bezeichner für Methodenname und Methode verwendet. 3
152
Eine Methode hat hauptsächlich einen Unterschied zu einer Funktion: Sie kann mit this auf Eigenschaften und andere Methoden des Objekts zugreifen. Sie erbt also gewissermaßen die anderen Eigenschaften und Methoden des Objekts.
Eigene Objekte
Beachten Sie, dass der Methodenaufruf ohne runde Klammern erfolgt. Eigenschaften aus dem Objekt stehen in der Methode mit dem Schlüsselwort this zur Verfügung.
TIPP
Das folgende Beispiel definiert eine Methode Laut_geben(), die auf die Eigenschaft Laut zugreift. Die Methode hat für die interne Verwendung denselben Namen wie für die externe außerhalb des Objekts. Listing 5.3:
1
Eine Methode definieren und verwenden (methode_eigene.html)
2
Methode
6
7 Abbildung 5.8: Der Text wird nun mit einer Methode ausgegeben.
8
9
10
11 Sollten Sie Variablen in einem Objekt oder in einer Methode verwenden, sind die Variablen dort natürlich lokal (siehe Kapitel 4 »Funktionen«). Lokale Variablen in Objekten heißen auch privat, da JavaScript keine eigenen Schlüsselwörter besitzt, um Klassen oder ihre Elemente als privat oder öffentlich (private oder public) zu kennzeichnen.
INFO
12
13
153
OOP
5.2.3
Parameter
Objekte können – wie Funktionen – auch Parameter erhalten, die dann in Eigenschaften und Methoden eingesetzt werden. Das Prinzip ist sehr einfach: function Objektname(Parameter1, Parameter2) { this.Eigenschaft1 = Parameter1; this.Eigenschaft2 = Parameter2 - Parameter1; }
Die Parameter werden übergeben und können dann in den Eigenschaften des Objekts genutzt werden. Der Aufruf der Objektinstanz sieht folgendermaßen aus: var Objektinstanz = new Objektname(Parameter1, Parameter2);
Etwas weniger theoretisch lässt sich das in einem Beispiel zeigen. Das Skript definiert ein Objekt Vogel. Als Parameter wird der Laut übergeben, den der Vogel gibt. Er wird dann von der Methode Laut_geben verwendet, um den Vogellaut auszugeben. Listing 5.4:
Einen Parameter übergeben (parameter.html)
Parameter
Abbildung 5.9: Der Vogel übernimmt den Laut aus dem Parameter.
154
Eigene Objekte
Standardwerte Der Fall, dass kein Parameter übergeben wird, obwohl er für eine Eigenschaft erforderlich wäre, ist natürlich sehr ärgerlich. Wenn beispielsweise im oberen Beispiel der Parameter laut nicht angegeben wird, gibt die Methode Laut_geben den Wert undefined zurück. var Wuschl = new Vogel(); Wuschl.Laut_geben();
1
Um dies zu vermeiden, können Sie in der Eigenschaft mit dem logischen ODER einen Alternativwert angeben:
2
function Vogel(laut) { this.Laut = laut || "Kikeriki"; this.Laut_geben = Laut_geben; }
3
Nun verwendet der Funktionsaufruf den Standardwert Kikeriki, da der Parameter laut keinen Wert hat, also null ist.
4 Abbildung 5.10: Der Interpreter greift auf den Standardwert zurück.
5 6
7
8
Standardwerte funktionieren erst ab der vierten Generation von Netscape Navigator und Internet Explorer. In älteren Versionen erscheint zwar keine Fehlermeldung, der logische Operator wird allerdings tatsächlich geprüft. Ist ein Parameter angegeben, der nicht gleich dem Standardwert ist, erscheint also beispielsweise true. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
9
Tabelle 5.2: Standardwerte
10
11
Parameter in Methoden 12
Natürlich können auch Methoden eigene Parameter haben. Dies macht allerdings eigentlich nur Sinn, wenn die Methode dennoch Eigenschaften vom Objekt erbt. Ansonsten besteht (fast) kein Unterschied mehr zu einer normalen Funktion mit Parametern.
13
155
OOP
Das folgende Beispiel macht durchaus Sinn. Dort wird ein Parameter an das Objekt übermittelt (siehe Listing 5.4), den zweiten Parameter erhält die Methode dagegen direkt beim Aufruf. In der Methode werden die beiden Zeichenketten dann zusammengesetzt: Listing 5.5:
Einen Parameter direkt an die Methode übermitteln (parameter_methoden.html).
Parameter und Standardwerte
Abbildung 5.11: Die beiden Parameter sind zu einer Ausgabe zusammengesetzt.
5.2.4 Konstruktor In anderen objektorientierten Sprachen gibt es meist eine Methode, die ausgeführt wird, wenn ein neues Objekt instanziiert wird. Diese Methode heißt Konstruktor. Das Gegenstück, eine Methode, die beim Löschen eines Objekts ausgeführt wird, heißt Destruktor. Der Destruktor ist in JavaScript nicht nachbildbar, wohl aber der Konstruktor. Alle Anweisungen, die beim Erstellen des Objekts ausgeführt werden sollen, packen Sie einfach zu den Eigenschaften direkt in die geschweiften Klammern des function-Schlüsselworts. Im folgenden Beispiel wird beim Erstellen des Objekts der Wert der Eigenschaft Laut ausgegeben:
156
Eigene Objekte
Listing 5.6:
Alle Anweisungen direkt innerhalb von function sind der »Konstruktor«
(konstruktor.html). Konstruktor
3
4
5.2.5 Sinn und Unsinn von eigenen Objekten Die objektorientierte Programmierung wird hauptsächlich dafür gerühmt, dass sie komplexe Zusammenhänge auf leicht verständliche Objekte überträgt. Die einfach strukturierbaren Beziehungen der Objekte zueinander und die wiederverwendbaren Eigenschaften und Methoden erleichtern dem Programmierer das Leben.4
5
Viele in JavaScript geschriebene Skripten und Programme sind nicht so komplex. In der Praxis kommt es relativ selten vor, dass ein Skript 100 oder 200 Zeilen überschreitet. Bei solchen Aufgabenstellungen ist es auch für professionelle JavaScript-Programmierer meist ausreichend, mit normalen Funktionen zu arbeiten. Anders ist das natürlich bei komplexeren Anwendungen im Web 2.0 und vor allem rund um Ajax. Hier kann die Objektorientierung helfen.
7
6
8
9
Allerdings werden hier manchmal die Besonderheiten der JavaScript-OOP zum Verhängnis. Es gibt in JavaScript beispielsweise keine Namensräume für Objekte, sprich, wenn Sie viele Bibliotheken einfügen und die Objekte darin denselben Namen haben, überschreiben sie einander. Außerdem sind viele Funktionen von Interfaces bis hin zu abstrakten Klassen in JavaScript nicht implementiert. Andere Funktionen wie die Vererbung entsprechen nicht dem in der heutigen Objektorientierung »Üblichen«. Einige JavaScript- und Ajax-Frameworks versuchen, die grundlegenden OOPMängel von JavaScript zu beseitigen. So besitzt beispielsweise das Microsoft ASP.NET Ajax-Framework eine Erweiterung, die Interfaces – Schnittstellen, die die Klassenstruktur vorgeben – simuliert.
4
10
11
12 INFO
13
Diese Analyse ist bewusst einfach gehalten. Es geht hier nicht um eine akademische Diskussion, sondern um des Pudels Kern, also die Konsequenzen für den aufstrebenden JavaScript-Programmierer.
157
OOP
5.3
for-in für Objekte
Die for-in-Schleife eignet sich nicht nur für Arrays5, sondern auch hervorragend, um auf die Eigenschaften und Methoden von Objekten zuzugreifen. Die Syntax ist sehr einfach: for (var Variable in Objekt) { Anweisungen }
Die Variable nimmt bei jedem Schleifendurchlauf einen Eigenschafts- oder Funktionsnamen aus dem Objekt auf, bis alle Schleifen durchlaufen sind. Auf den Inhalt des Objekts greifen Sie mit Objekt[Variable]
zu. Die for-in-Schleife ist in den Browsern ab Netscape 2 und Internet Explorer 3 in der ersten Version verfügbar. Tabelle 5.3: Die for-in-Schleife
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel liest alle Eigenschaften und Methoden des Objekts Wuschl und gibt sie auf dem Bildschirm aus: Listing 5.7:
Mit for-in auf ein Objekt zugreifen (forin_objekte.html)
for-in zum Objektzugriff
5
158
Siehe Abschnitt 7.1.4 »for-in- und andere Schleifen«.
with
Bei der Ausgabe ist besonders interessant, dass bei der Methode die gesamte Funktion ausgegeben wird (siehe Abbildung 5.12). Abbildung 5.12: Die for-in-Schleife gibt Eigenschaften und Methoden aus.
1
2
3
4
5.4
with
5
Die with-Anweisung ist eine vereinfachte Schreibweise, wenn Sie auf mehrere Eigenschaften und Methoden eines Objekts zugreifen müssen. Sie schreiben das Objekt einmal in die with-Anweisung und können die Eigenschaften und Methoden dann ohne Objektnamen aufrufen:
6
with(Objekt) { Eigenschaft Methode }
7
8
with ist in den Browsern ab Netscape 2 und Internet Explorer 3 in der ersten Version integriert.
with
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 5.4: Die with-Anweisung
Als Ausgangspunkt für ein Beispiel dient Listing 5.3. Aus diesem Listing kommt das Objekt. Die Objektinstanz und der Eigenschaften- bzw. Methodenaufruf mit with sehen wie folgt aus:
9
10
11
Listing 5.8: Die with-Anweisung (with.html)
12
var objWuschl = new Wuschl(); with(objWuschl) { document.write(Laut + ""); document.write(Vorkommen + ""); Laut_geben(); }
13
159
OOP
Abbildung 5.13: Die Eigenschaften und Methoden werden ausgegeben.
TIPP
In der Praxis ist die Verwendung von with dann sinnvoll, wenn Sie Eigenschaften und Methoden eines Objekts öfter hintereinander benötigen. Der Tippaufwand und damit eine mögliche Fehlerquelle verringern sich.
5.5
prototype-Vererbung
JavaScript hat – wie bereits erwähnt – keine Vererbung über Klassen. Aus anderen Programmiersprachen kennen Sie vielleicht das Schlüsselwort inherits (erbt von). Diese Art von Vererbung gibt es in JavaScript nicht, denn Vererbung wird hier erst zur Laufzeit festgelegt und nicht im Quellcode per Schlüsselwort. An die Stelle der normalen Vererbung tritt die prototype-Vererbung. Das Schlüsselwort prototype hat nur wenig mit dem Prototyp zu tun, den Sie vielleicht mit einem neuen Auto oder Computer in der Entwicklung verbinden. prototype ist eine Eigenschaft, die für jedes Objekt zur Verfügung steht, unabhängig davon, ob selbst definiert oder in JavaScript enthalten. Jedes Objekt hat also eine eigene Eigenschaft prototype. Die prototype-Eigenschaft ist wiederum ein Objekt. Klingt das verwirrend? Mit einem Beispiel wird es klarer. Eine Klasse Vogel enthält Eigenschaften und Methoden. Die Eigenschaft prototype ist immer vorhanden. Diese Eigenschaft verweist (referenziert) auf das zugehörige prototype-Objekt Vogel.prototype. Dieses Objekt wiederum kann eigene Eigenschaften und Methoden besitzen (siehe Abbildung 5.14). Abbildung 5.14: prototypeVererbung
160
prototype-Vererbung
Die Eigenschaften und Methoden des Objekts Vogel.prototype erbt das Objekt Vogel. Wenn Sie also die Eigenschaft Laut für das prototype-Objekt von Vogel definieren, sieht dies folgendermaßen aus: Vogel.prototype.Laut = "Tschiep";
Die Eigenschaft des prototype-Objekts erbt nun das Vogel-Objekt. 1
Vogel.Laut;
liefert also den Wert »Tschiep«, der ursprünglich im prototype-Objekt definiert wurde.
2
Wichtig bei dieser Vererbung ist, dass der JavaScript-Interpreter immer wissen muss, woher er die jeweilige Eigenschaft (oder Methode) nimmt. Wird eine Eigenschaft des Objekts, wie z.B. Vogel.Laut, aufgerufen, prüft der Interpreter zuerst, ob das Objekt die Eigenschaft besitzt. Ist dies – wie in diesem Beispiel – nicht der Fall, überprüft er, ob das prototype-Objekt eine solche Eigenschaft besitzt. Erst wenn dies auch nicht der Fall ist, gibt er undefined zurück.
3
4
prototype steht seit Netscape Navigator 3 und Internet Explorer 4 zur Verfügung. NS4.x
prototype
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
5 Tabelle 5.5: prototype
6
7
Das folgende Beispiel zeigt Abbildung 5.14 in ausprogrammierter Form: Listing 5.9:
Vererbung mit prototype (prototype.html)
8
prototype-Vererbung
12
13
161
OOP
Die Eigenschaft Laut wird im prototype-Objekt erst nach der Definition des Objekts Vogel und sogar erst nach der Objektinstanziierung von Wuschl festgelegt. Dennoch steht sie bereits nach ihrer Deklaration auch für die Objektinstanz Wuschl zur Verfügung.6 Abbildung 5.15: Die zweite Eigenschaft wurde nachträglich per prototypeVererbung zugewiesen und mit einer Methode des ursprünglichen Objekts ausgegeben.
TIPP
Die prototype-Vererbung gilt nur für das Lesen von Eigenschaften. Setzen Sie eine Eigenschaft, wird die Eigenschaft für das Objekt selbst deklariert, nicht aber für das prototype-Objekt. Vogel.Laut = "Krächz";
ändert also nicht die Eigenschaft Laut des prototype-Objekts.
5.6
Das Objekt Object und die Vererbungskette
JavaScript hat ein eigenes Objekt namens Object. Es spielt eine Sonderrolle: Dieses Objekt ist eine Art Überobjekt für alle anderen Objekte in JavaScript. Das Object-Objekt gibt es seit Netscape Navigator 3 und Internet Explorer 4. Tabelle 5.6: Das ObjectObjekt
Object
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Mit dem Object-Objekt können Sie verschiedene Aktionen durchführen: Sie können ein eigenes Objekt auch mit Object definieren (siehe Abschnitt 5.6.1 »Objekte mit Object definieren«). Außerdem stehen die Eigenschaften und Methoden von Object in allen anderen Objekten zur Verfügung (siehe Abschnitt 5.6.2 »Eigenschaften und Methoden von Object«). Die interessanteste Möglichkeit ergibt sich allerdings für die prototype-Vererbung. Weisen Sie eine Eigenschaft oder Methode dem prototype-Objekt des Object-Objekts zu, steht sie in allen Objekten von JavaScript zur Verfügung.7 6 7
162
Unabhängig davon steht eine prototype-Eigenschaft oder -Methode für alle Objektinstanzen des Objekts zur Verfügung. Dieser Vorgang bildet in JavaScript eine so genannte Vererbungskette.
Das Objekt Object und die Vererbungskette
Das folgende Beispiel zeigt dies. Hier wird dem prototype-Objekt des Object-Objekts die Eigenschaft Laut zugewiesen. Diese Eigenschaft kann nun von der Methode Laut_geben des Objekts Wuschl (Instanz von Vogel) verwendet werden. Aber nicht nur die macht davon Gebrauch: Auch als Eigenschaft des JavaScript-eigenen Date-Objekts ist Laut verfügbar: Listing 5.10: prototype-Vererbung mit Object (prototype_object.html)
1
prototype-Vererbung mit Object
Besitzt ein Objekt eine Eigenschaft mit dem gesuchten Namen, hier also Laut, wird diese verwendet. Dann wird im prototype-Objekt nachgesehen. Erst zum Schluss greift der Interpreter auf die Eigenschaft im prototype-Objekt des Object-Objekts zurück.
5 6
7
8
INFO
9 Abbildung 5.16: Die Eigenschaft Laut ist für alle Objekte definiert.
10
11
12 In Abschnitt 5.7 »Vorhandene Objekte erweitern« sehen Sie, wie JavaScripteigene Objekte mit prototype um neue Eigenschaften und Methoden erweitert werden.
13
REF
163
OOP
5.6.1
Objekte mit Object definieren
Wenn Sie ein neues Object-Objekt instanziieren, entsteht damit ein eigenes Objekt: var Objektname = new Object();
Diese Objektdefinition ist also eine Alternative zur Definition mit function.
INFO
Da die Definition über function auch bereits in älteren Browsern wie Netscape Navigator 2 oder Internet Explorer 3 in beiden Versionen zur Verfügung steht, wird diese Variante in der Praxis häufig bevorzugt. Allerdings können diese beiden Browser mittlerweile ignoriert werden. Das folgende Skript zeigt eine Objektdeklaration mit Object: Listing 5.11: Ein eigenes Objekt mit Object (object.html) Eigene Objekte mit Object
Abbildung 5.17: Die Methode für das mit Object definierte Objekt liefert die richtige Ausgabe.
5.6.2 Eigenschaften und Methoden von Object Das Object-Objekt besitzt einige Eigenschaften und Methoden, die Sie in den nächsten Abschnitten beschrieben finden. Wie bereits erwähnt, stehen sie für alle Objekte in JavaScript zur Verfügung. Die Eigenschaften und Methoden des Object-Objekts sind neueren Datums (siehe Tabelle 5.7), fehlen also in älteren Browsern.
164
Das Objekt Object und die Vererbungskette
NS4.x
Object
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 5.7: Eigenschaften und Methoden von Object
hasOwnProperty() Die Methode hasOwnProperty("Eigenschaft") überprüft, ob eine Objektinstanz eine Eigenschaft besitzt. Wenn dies der Fall ist, liefert sie true, sonst false. Der Name der Eigenschaft wird als Parameter übergeben. Beachten Sie, dass er ein String sein muss, also in Anführungszeichen steht.
1
2
Das folgende Beispiel testet, ob die Eigenschaft Vorkommen in der Objektinstanz Wuschl vorhanden ist. 3
Listing 5.12: hasOwnProperty() im Einsatz (hasOwnProperty.html) hasOwnProperty()
6
Die Methode hasOwnProperty() liefert im Beispiel true, da eine Eigenschaft mit dem Namen vorhanden ist (siehe Abbildung 5.18).
8
7
Abbildung 5.18: Die Eigenschaft ist vorhanden.
9
10
11
12 Eine per prototype zugewiesene Eigenschaft wird in die Überprüfung nicht mit einbezogen: document.write(Wuschl.hasOwnProperty("Laut"));
13
TIPP
liefert also false.
165
OOP
propertyIsEnumerable() propertyIsEnumerable("Eigenschaft") ist eigentlich eine Eigenschaft, die aber ähnlich wie die Methode hasOwnProperty("Eigenschaft") arbeitet.8 Sie geben eine Eigenschaft als Parameter an und propertyIsEnumerable() liefert zurück, ob das Objekt die Eigenschaft besitzt (true) oder nicht (false).
In Listing 5.12 ändert sich nur die Ausgabezeile mit dem Zugriff auf die Eigenschaft propertyIsEnumerable() statt hasOwnProperty(): Listing 5.13: propertyIsEnumerable() (propertyIsEnumerable.html) document.write(Wuschl.propertyIsEnumerable("Vorkommen"));
Die Ausgabe ist dieselbe, wie es in Abbildung 5.18 zu sehen ist. isPrototypeOf() Die Methode isPrototypeOf(Objekt) liefert als Ergebnis, ob ein Objekt in einer prototype-Verknüpfung mit dem aktuellen Objekt steht: Objekt.isPrototypeOf(Objekt);
Diese Methode wird in der Praxis recht selten benötigt. Nachfolgend finden Sie dennoch ein einfaches Beispiel: Listing 5.14: isPrototypeOf() (isPrototypeOf.html) isPrototypeOf()
Das Skript prüft, ob das Objekt Vogel.prototype eine prototype-Verknüpfung mit der Objektinstanz Wuschl besitzt. Das Ergebnis ist true (siehe Abbildung 5.19). Dies entspricht natürlich auch den Erwartungen, da Wuschl eine Objektinstanz von Vogel ist und Vogel.prototype daher auch als prototype-Objekt für Wuschl verfügbar ist.
8
166
Microsoft nennt propertyIsEnumerable() eine Eigenschaft (beispielsweise http://msdn.microsoft. com/library/default.asp?url=/library/en-us/jscript7/html/jsproPropertyIsEnumerable.asp). Der Autor des Buches würde sie eher als Methode bezeichnen. Dies ist allerdings eine in der Praxis nicht relevante Diskussion.
Das Objekt Object und die Vererbungskette
Abbildung 5.19: true besagt, dass Wuschl eine prototypeVerbindung mit dem Objekt Vogel.prototype besitzt.
1
2 toString(), toLocaleString() und valueOf() Die Funktionen toString(), toLocaleString() und valueOf() stehen über das Object-Objekt jedem Objekt zur Verfügung: ■ ■
■
3
toString()– wandelt das Objekt in eine Zeichenkette um. toLocaleString()– wandelt ebenfalls in eine Zeichenkette um, berücksichtigt aber lokale Einstellungen, die beispielsweise in der Sprachversion liegen können. Dies hat nur bei manchen Objekten wie Date (siehe Kapitel 6 »Objekte in JavaScript«) Auswirkungen. valueOf()– liefert den Wert eines Objekts. Bei einem Number-Objekt ist das eine Zahl, bei einem String-Objekt beispielsweise ein String.
4
5
Im folgenden Beispiel werden die drei Methoden auf ein eigenes Objekt angewendet:
6
Listing 5.15: Objekt in String umwandeln (toString_toLocaleString_valueOf.html)
7
function Vogel(vork) { this.Vorkommen = vork; }
8
var Wuschl = new Vogel("Europa"); document.write(Wuschl.toString() + ""); document.write(Wuschl.toLocaleString() + ""); document.write(Wuschl.valueOf());
9
Abbildung 5.20: Dreimal dieselbe Ausgabe
10
11
12
13
167
OOP
TIPP
Die drei hier vorgestellten Methoden machen meist nur bei speziellen Objekten wie beispielsweise Date Sinn. Bei eigenen Objekten wird nur der Datentyp ausgegeben (siehe Abbildung 5.20). Für eigene Objekte kann man toString() auch selbst implementieren.
5.7
Vorhandene Objekte erweitern
prototype bietet noch mehr Möglichkeiten als nur die Vererbung in eigenen Objekten. Auch bestehende JavaScript-Objekte können so erweitert werden. Ein mögliches Anwendungsgebiet ist, Methoden für ältere Browserversionen selbst zu schreiben, die nur in neueren Browsern vorhanden sind. Dieser Abschnitt zeigt Ihnen, wie Sie ein bestehendes Objekt um eine eigene Funktionalität erweitern.
Als Beispiel dient das Date-Objekt. Es soll eine Methode erhalten, die das Datum in ein in Deutschland übliches Format umwandelt. Das Beispiel besteht aus zwei Teilen: ■ ■
Einer externen JavaScript-Datei mit der Endung .js. Sie enthält den Quellcode der Methode und definiert die Methode für das Date-Objekt. Einer HTML-Datei, die die externe JavaScript-Datei lädt und in einem weiteren Skriptblock die neue Methode von Date verwendet.
Die externe Datei enthält zuerst eine Definition der neuen Methode: Date.prototype.deformat = de_format;
Die neue Methode heißt also deformat und wird als Methode des prototypeObjekts definiert. Sie könnten die Methode natürlich sofort bei der Zuweisung mit function beginnen lassen. Hier ist es allerdings günstiger, den Code über den Funktionsnamen (de_format) aufzurufen, da das Listing sonst unübersichtlich wird.
WWW
Die Methode selbst ist eine Abwandlung der Datei datum_formatieren_ y2k.html aus Kapitel 6 »Objekte in JavaScript«. Sie finden sie im DownloadArchiv im Ordner code\kap06. In der Funktion wurde hauptsächlich das aktuelle Datum durch das Schlüsselwort this ersetzt, denn die Methode soll ja immer auf das gerade aktuelle Datumsobjekt angewendet werden können. Darüber hinaus musste die Ausgabe mit document.write() der Rückgabe einer Ergebnisvariablen (return erg;) weichen.
168
Vorhandene Objekte erweitern
Hier der vollständige Code: Listing 5.16: Die externe JavaScript-Datei (erweitern.js) Date.prototype.deformat = de_format; function de_format() { function getJahr2000(datum) { var jahr = datum.getYear(); if (jahr < 200) { return jahr + 1900; } else { return jahr; } }
1
2
3
var monat = new Array(12); monat[0] = "Januar"; monat[1] = "Februar"; monat[2] = "März"; monat[3] = "April"; monat[4] = "Mai"; monat[5] = "Juni"; monat[6] = "Juli"; monat[7] = "August"; monat[8] = "September"; monat[9] = "Oktober"; monat[10] = "November"; monat[11] = "Dezember";
4
5 6
7
var erg = this.getDate() + ". "; erg += monat[this.getMonth()] + " "; erg += getJahr2000(this); return erg;
8
}
Nun zur HTML-Seite. Um die Methode dort anzuwenden, müssen Sie nur eines tun: Binden Sie die externe JavaScript-Datei in ein -Tag ein. Auf die Methode wird ganz gewöhnlich mit dem Methodennamen zugegriffen. Das Objekt im Beispiel wurde vorher instanziiert.
9
10
Nachfolgend sehen Sie den Code: Listing 5.17: In der HTML-Seite wird die neue Methode des Date-Objekts verwendet (erweitern.html)
11
Objekte erweitern
In unserem Beispiel verwenden wir das Tagesdatum (siehe Abbildung 5.21). Sie können aber natürlich auch ein beliebiges Datum von der neuen Methode sauber formatieren lassen. Abbildung 5.21: Das Datum, sauber formatiert
Dieses Beispiel gibt einen kleinen Ausblick auf die Möglichkeiten, Objekte zu erweitern. Die Vorteile liegen auf der Hand: TIPP
■ ■
■
Sie müssen nur eine externe JavaScript-Datei einbinden. Dann können Sie alle darin definierten Eigenschaften und Methoden nutzen. Die externe Datei lässt sich natürlich in beliebig vielen Seiten wiederverwenden. So schaffen Sie sich eine eigene kleine Eigenschaften- und Methoden-Bibliothek. Methoden, die nur auf neueren Browsern laufen, können – selbst programmiert – auch auf älteren Browsern zur Mitarbeit bewegt werden.
5.7.1
Math erweitern
Das Math-Objekt verhält sich beim Erweitern ein wenig anders. Hier verwenden Sie nicht das prototype-Objekt, sondern weisen die neue Methode direkt zu. Der Grund dafür: Math ist ein global verfügbares Objekt, das keine Objektinstanzen besitzt. Ein Beispiel erklärt dies. Ziel ist es, eine Methode zu definieren, die die Fakultät9 einer als Parameter übergebenen Zahl zurückliefert. Die Methode selbst befindet sich wieder in einer externen Datei. Dieses Mal wird sie dem Math-Objekt allerdings ohne prototype zugewiesen: Math.fak = fakultaet;
9
170
Die Fakultät einer Zahl ist das Produkt aller ganzen Zahlen, die kleiner oder gleich der Zahl und größer 0 sind (n * [n-1] * [n-2] … * 1). Die Fakultät von 3 ist also 3*2*1 = 6. Geschrieben wird die Fakultät auch häufig mit nachgestelltem Ausrufezeichen, also n! bzw. 3!.
Vorhandene Objekte erweitern
Hier sehen Sie den kompletten Code einschließlich der Funktion zur Berechnung der Fakultät: Listing 5.18: Math um eine Methode zur Fakultätsberechnung erweitern (erweitern_math.js) Math.fak = fakultaet; function fakultaet(x) { x = Math.round(x); var wert = 1; if(x > 0) { for (var i = x; i > 0; i--) { wert *= i; } } else { wert = "Zahl größer 0 angeben"; } return wert; }
1
2
3
4
Der Aufruf erfolgt wie bereits im letzten Beispiel besprochen. Die externe JavaScript-Datei wird eingebunden, die Methode einfach verwendet. Das folgende Skript gibt also die Fakultät von 3 aus:
5
Listing 5.19: Eine neue Methode des Math-Objekts verwenden (erweitern_mathe.html)
6
Objekte erweitern
7
8
9 Abbildung 5.22: Die Fakultät von 3 ist 6.
10
11
12
13
171
OOP
5.8
Funktionen und Objekte
Eine Funktion kann in JavaScript selbst als Objekt definiert werden. Für das dazugehörige Objekt function gibt es auch eigene Eigenschaften und Methoden. Was sich nach keiner besonders tollen Nachricht anhört, bietet doch einige Möglichkeiten, wie Sie in den folgenden Abschnitten sehen werden. Der letzte Teil dieses Kapitels beschäftigt sich mit Objekten, die als Parameter an Funktionen übergeben werden.
5.8.1
Funktionen als Objekt
Um Funktionen als Objekte zu deklarieren, bietet JavaScript eigens das Function-Objekt. Eine neue Funktion wird als Variable zugewiesen und mit dem Konstruktor new initialisiert: var Name = new Function(Par1, Par2,…, Anweisungen);
In runden Klammern folgen zuerst die Parameter der Funktion. Sie müssen als Strings übergeben werden, also in Anführungszeichen stehen. JavaScript erlaubt bis zu 255 Operanden für ein Function-Objekt. INFO
TIPP
Nach den Parametern folgen die Funktionsanweisungen. Sie stehen ebenfalls in Anführungszeichen. Die Parameter sind optional, dürfen also alle fehlen. Die Anweisungen müssen dagegen vorhanden sein. Auch eine Funktion, die auf herkömmliche Art und Weise (siehe Kapitel 4 »Funktionen«) deklariert wurde, wird von JavaScript als eigenes FunctionObjekt angesehen. Sie können darauf also die Eigenschaften und Methoden anwenden (siehe die folgenden zwei Abschnitte). Das folgende Beispiel erzeugt ein Function-Objekt, das das Produkt zweier Zahlen zurückgibt: Listing 5.20: Eine Funktion als Objekt (function_object.html) Funktionen als Objekte
172
Funktionen und Objekte
Abbildung 5.23: Das Produkt von 3 und 4
1
2 Das Function-Objekt ist in allen Browsern ab Netscape 3 und Internet Explorer 3 in der zweiten Version integriert. In den vorhergehenden Versionen gibt es das Objekt bereits theoretisch, allerdings ist die Implementation sehr unsauber.
3
4 Function
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 5.8: Das Objekt Function
5
5.8.2 Eigenschaften von Function
6
Das Function-Objekt bietet natürlich noch einige nützliche Eigenschaften. In der folgenden Tabelle finden Sie eine Zusammenfassung und anschließend einige Beispiele: Eigenschaft
Beschreibung
arguments
Ein Array mit allen Parametern des Function-Objekts.
arguments.callee
Enthält die Anweisungen der aktuell aufgerufenen Funktion.
arguments.caller
Enthält den Namen der Funktion, die die aktuelle Funktion ausführt. Da nicht Teil des ECMAScript 262-Standards, wurde die Eigenschaft aus Netscape Navigator ab Version 6 verbannt.
arguments.length
Liefert die Zahl der übergebenen Parameter.
arity
Enthält die Zahl der von der Funktion erwarteten Parameter. Ist auf Netscape 4.x, 6 und 7 sowie Mozilla beschränkt. Daher sollte length verwendet werden.
length
7 Tabelle 5.9: Eigenschaften des FunctionObjekts
8
9
10
11
Enthält die Zahl der von der Funktion erwarteten Parameter. Ist noch nicht in Netscape 3 enthalten; ersetzt heutzutage arity.
12
Die folgende Tabelle enthält die Browserkompatibilität der verschiedenen Eigenschaften des Function-Objekts.
13
173
OOP
Tabelle 5.10: Eigenschaften des FunctionObjekts
NS4.x M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
arguments
arguments.callee
arguments.caller
arguments.length
aritya length a.
Gilt seit JavaScript 1.4 als veraltet.
Das folgende Beispiel zeigt die Eigenschaften arguments und arguments.length im Einsatz: Listing 5.21: Mit Eigenschaften von Function arbeiten (arguments.html) arguments
Abbildung 5.24: Der Browser gibt die einzelnen Argumente und das Ergebnis aus.
174
Funktionen und Objekte
Im Download-Archiv finden Sie zu jeder der nicht im Beispiel verwendeten Eigenschaften ein eigenes Beispiel im Ordner code\kap05. Die Datei trägt den Namen der Eigenschaft.
WWW
5.8.3 Methoden von Function 1
Bei den Methoden des Function-Objekts sind vor allem zwei, apply() und call(), beachtenswert. Zu beiden Methoden finden Sie im Anschluss an die Übersichtstabelle ein Beispiel. Methode
Beschreibung
apply(Objekt, Parameter)
Erlaubt Ihnen, eine Funktion oder Methode innerhalb eines anderen Objekts (angegeben als Parameter Objekt) anzuwenden, ohne dass eine direkte Verbindung besteht. Optional können Parameter in einem Array für die Methode übergeben werden (einen Array liefert beispielsweise die Eigenschaft arguments).
2 Tabelle 5.11: Eigenschaften des FunctionObjekts
3
4
call(Objekt, Par1, Par2, …)
Ruft innerhalb eines Objekts die Methode eines anderen Objekts auf. Der Name des anderen Objekts wird als Parameter übergeben. Optional können noch weitere Parameter an die Methode übergeben werden. Diese sind – und das ist der Unterschied zu apply – einzeln angegeben.
5
toSource()
Liefert die Anweisungen innerhalb einer Funktion als Quellcode.
6
toString()
Wandelt die Anweisungen einer Funktion in einen String um.
valueOf()
Liefert einen String mit den Anweisungen der Funktion. In der Praxis bestehen kaum Unterschiede zu toString().
7
8
In der folgenden Tabelle finden Sie die Browserkompatibilität der Methoden des Function-Objekts. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
apply()
call()
toSource()
toString()
valueOf()
9
Tabelle 5.12: Methoden des FunctionObjekts
10
11
12 Der Einsatz von apply() und call() lässt sich am einfachsten mit einem Beispiel erklären: ■
13
Im folgenden Code befinden sich ein Objekt Vogel und eine Funktion Laut_geben(). Die Funktion ist allerdings keine Methode von Vogel, da sie im Objekt nicht definiert wird. 175
OOP
function Vogel(laut) { this.Laut = laut; } function Laut_geben() { document.write(this.Laut); } ■
Als zweiter Schritt entsteht eine Objektinstanz von Vogel. var objVogel = new Vogel("Kroarg");
■
Nun kommt apply() ins Spiel. Mit dem folgenden Aufruf wird die Funktion Laut_geben als Methode für das instanziierte Objekt objVogel angewendet. Laut_geben.apply(objVogel);
■
call() hat hier die vollkommen gleiche Wirkung, da keine Parameter außer dem Objekt mit übergeben werden. Laut_geben.call(objVogel);
INFO
apply(Objekt, Parameter) übergibt optional ein Array mit Parametern an die aufgerufene Funktion. call(Objekt, Par1, Par2, …) überträgt dagegen alle Parameter nicht in ein Array, sondern einzeln.
Hier der komplette Code für das Beispiel: Listing 5.22: apply() und call() (apply_call.html) apply() und call()
WWW
176
Im Download-Archiv im Ordner code\kap05 finden Sie drei einfache Skripte für die anderen drei Methoden des Function-Objekts. Der Dateiname entspricht jeweils dem Funktionsnamen.
Funktionen und Objekte
Abbildung 5.25: apply() und call() ersetzen hier die Methodeneinbindung in das Objekt.
1
2
3
5.8.4 Objekte an Funktionen übergeben Objekte übergibt JavaScript als Referenz an eine Funktion. Das heißt, die Funktion erhält nicht das komplette Objekt, sondern nur einen Verweis darauf. Die Funktion selbst wird also als Original verändert, wenn mit der Referenz gearbeitet wird. Dies hat im Allgemeinen allerdings keine praktischen Auswirkungen.
4
5
Andere, einfachere Elemente wie Text, Zahlen und Wahrheitswerte werden nur als Wert übergeben. Das Original bleibt unverändert.
6 INFO
7
8
9
10
11
12
13
177
Inhalt
6
Objekte in JavaScript 1
2
JavaScript stellt eine Reihe von Objekten zur Verfügung. Mit diesen Objekten arbeiten Sie. Die Sprachsyntax, die die ersten Kapitel dieses Buches füllt, ist nur ein Hilfsmittel, um mit den vielen Objekten von JavaScript etwas anfangen zu können.
3
In JavaScript arbeiten Sie mit verschiedenartigen Objekten, die auch unterschiedliche Aufgaben erfüllen. Diese Objekte lassen sich grob in drei Kategorien unterteilen: Objekte, die der Browser mitbringt, Standardobjekte von JavaScript und proprietäre Objekte der verschiedenen Browser.
5
Browserobjekte – alle Objekte, die in Verbindung mit dem Browserfenster und dem Inhalt des Browsers stehen. Über diese Objekte wird der größte Teil der Arbeit in JavaScript bewältigt. Da diese Objekte browserspezifisch sind, bestehen zwischen den Browsern natürlich einige Unterschiede, die Sie bei der Beschreibung der einzelnen Objekte finden.
6 7
Abbildung 6.1 gibt einen Überblick über wichtige Browserobjekte. Die hier abgebildete Hierarchie heißt auch DOM (Document Object Model). Jeder Browser besitzt sein eigenes DOM. Allerdings wird vom W3C auch ein DOM standardisiert. Mehr zu diesem Thema erfahren Sie in Kapitel 8 »Document Object Model«. Die verschiedenen Browserobjekte finden Sie insbesondere im dritten Teil dieses Buches (»Webanwendungen«), denn sie bilden die Grundlage zur Arbeit mit JavaScript. Ohne Browserobjekte wäre der Zugriff auf Frames und Formulare nicht möglich. Sie könnten mit JavaScript keine neuen Fenster öffnen und noch nicht einmal mit document.write() Text ausgeben, denn document ist bereits eines der Objekte, die der Browser bereitstellt.
8
9 REF
10
11 Abbildung 6.1: Ein einfaches DOM der Browserobjekte
12
13
179
ndex
■
4
Objekte in JavaScript
TIPP
Die Browserobjekte sind kein Teil des JavaScript-Interpreters, sondern werden vom Browser auch anderen Skriptsprachen wie beispielsweise VBScript im Internet Explorer zur Verfügung gestellt. Der JavaScript-Interpreter beinhaltet nur die Schnittstelle zu diesen Objekten und damit zum DOM. ■
Eingebaute Standardobjekte1 – sind Teil des ECMAScript-Standards v3 und von JavaScript 1.5. Sie werden daher von den neueren Browsern unterstützt. Inkompatibilitäten und Probleme finden Sie bei den detaillierteren Beschreibungen der Objekte. Array – enthält Eigenschaften und Methoden für Arrays. Arrays werden in Kapitel 7 »Arrays und Strings« genauer beleuchtet. ■ Boolean – enthält die Wahrheitswerte (siehe Abschnitt 6.1.3 »Boolean«). ■ Date – dient zur Arbeit mit Daten und Uhrzeit. Eine Beschreibung finden Sie in Abschnitt 6.1.2 »Date«. ■ Function – ist das Objekt für Funktionen. Es wird in Kapitel 5 »OOP« erläutert. ■ Math – enthält mathematische Funktionen und Konstanten. Wie Sie damit rechnen, erfahren Sie im nächsten Abschnitt 6.1.1 »Math«. ■ Number – dient zur Arbeit mit dem Datentyp Zahl (siehe Abschnitt 6.1.4 »Number«). ■ Object – steht für den Datentyp Object bereit (siehe Kapitel 5 »OOP«). ■ RegExp – erlaubt reguläre Ausdrücke in JavaScript. Sie werden in Kapitel 21 behandelt. ■ String – ermöglicht die Arbeit mit Zeichenketten. Dieses Objekt ist Teil von Kapitel 7 »Arrays und Strings«. ■ Error – enthält eine entsprechende Fehlermeldung. Mehr zum Fehlerhandling erfahren Sie in Kapitel 29, »Debugging«. Browserspezifische eingebaute Objekte – sind alle Objekte, die keine Browserobjekte und nicht Teil des Standards sind. Eine Beschreibung dieser Objekte finden Sie in Abschnitt 6.1.7 »Proprietäre Objekte«. ■
■
6.1.1
Math
Das Math-Objekt beinhaltet mathematische Konstanten und Funktionen. Die Konstanten sind natürlich Eigenschaften, die Funktionen dagegen Methoden, da sie etwas ausführen.
1
180
In Unterscheidung zu den Browserobjekten werden sie auch häufig Standardklassen genannt.
Objekte in JavaScript
Das Math-Objekt muss nicht mit new instanziiert werden. Es kann direkt aufgerufen werden und steht immer zur Verfügung. INFO
Tabelle 6.2 und Tabelle 6.3 zeigen die Eigenschaften und Methoden des Math-Objekts im Überblick. Anschließend finden Sie Erklärungen und Beispiele zum Rechnen, Runden und zu Zufallszahlen.
1
Das Math-Objekt und seine Eigenschaften und Methoden werden in allen gängigen Browsern unterstützt und sind bereits ab JavaScript 1.0 vorhanden.
Math
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Eigenschaft
Beschreibung
Wert
E
die Konstante e (eulersche Zahl)
2.718281828459045
LN2
der natürliche Logarithmus von 2
0.6931471805599453
LN10
der natürliche Logarithmus von 10
2.302585092994046
LOG2E
der Logarithmus von e zur Basis 2
1.4426950408889633
LOG10E
der Logarithmus von e zur Basis 10
0.4342944819032518
PI
die Kreiszahl π (gesprochen PI)
3.141592653589793
SQRT1_2
Quadratwurzel von ½
0.7071067811865476
SQRT2
Quadratwurzel von 2
1.4142135623730951
2 Tabelle 6.1: Das Math-Objekt
Tabelle 6.2: Die Eigenschaften des Math-Objekts
3
4
5
6 7
8
Die Tabelle enthält die Genauigkeit, in der JavaScript die mathematischen Konstanten speichert und für Berechnungen zur Verfügung stellt.
9
Im Download-Archiv finden Sie die Datei math_eigenschaften.html, in der alle Eigenschaften ausgegeben werden.
10 WWW
Tabelle 6.3: Die Methoden des Math-Objekts
11
Methode
Beschreibung
Beispiel
abs(Zahl)
Absolutwert einer Zahl
x = Math.abs(-4); //Erg: 4
acos(Zahl)
Arkuskosinus einer Zahl
x = Math.acos(0.4); //Erg: 1.1592794807274085
12
asin(Zahl)
Arkussinus einer Zahl
x = Math.asin(1); //Erg: 1.5707963267948965
13
atan(Zahl)
Arkustangens einer Zahl
x = Math.atan(2); //Erg: 1.1071487177940904
181
Objekte in JavaScript
Tabelle 6.3: Die Methoden des Math-Objekts (Forts.)
Methode
Beschreibung
Beispiel
atan2(Gegenkathete, Ankathete)
Arkustangens der Polarkoordinaten der Gegenkathete und Ankathete
x = Math.atan2(2,3); //Erg: 0.5880026035475675
ceil(Zahl)
nächster ganzzahliger Wert, der größer oder gleich der angegebenen Zahl ist
x = Math.ceil(4.3); //Erg: 5
cos(Zahl)
Kosinus einer Zahl
x = Math.cos(-1); //Erg: 0.5403023058681398
exp(Zahl)
eZahl; die Zahl als Potenz der eulerschen Zahl
x = Math.exp(2); //Erg: 7.38905609893065
floor(Zahl)
nächster ganzzahliger Wert, der kleiner oder gleich der angegebenen Zahl ist
x = Math.floor(4.3); //Erg: 4
log(Zahl)
natürlicher Logarithmus der Zahl; ein natürlicher Logarithmus hat die Basis e, also die eulersche Zahl
x = Math.log(10); // Erg: 2.302585092994046
max(Zahl1, Zahl2)
liefert die größere der beiden Zahlen
x = Math.max(4, 3); //Erg: 4
min(Zahl1, Zahl2)
liefert die kleinere der beiden Zahlen
x = Math.min(4, 3); //Erg: 3
ZahlPotenz; liefert als Ergebnis eine potenzierte
x = Math.pow(2, 3); //Erg: 8
pow(Zahl, Potenz)
HALT
182
Zahl
random()
ergibt eine Zufallszahl zwischen 0 und 1 (siehe Abschnitt »Zufallszahlen, Seite 189«)
round(Zahl)
rundet eine Zahl auf die nächste ganze Zahl auf, x = Math.round(4.56); wenn sie hinter dem Komma größer gleich .5 ist, //Erg: 5 ansonsten wird abgerundet (siehe Abschnitt »Runden, Seite 186«)
sin(Zahl)
Sinus einer Zahl
var x = Math.sin(2); //Erg: 0.9092974268256817
sqrt(Zahl)
Quadratwurzel (v) einer Zahl
x = Math.sqrt(9); //Erg: 3
tan(Zahl)
Tangens einer Zahl
x = Math.tan(0.4); //Erg: 0.4227932187381618
x = Math.random;
Bei einigen Funktionen sind manche Werte nicht erlaubt, beispielsweise müssen die Zahlen (im Bogenmaß) beim Arkuskosinus zwischen –1 und 1 liegen. Ist dies nicht der Fall, gibt der JavaScript-Interpreter als Ergebnis NaN zurück.
Objekte in JavaScript
Im Download-Archiv zum Buch finden Sie im Ordner code/kap6 alle Methoden des Math-Objekts als Beispieldatei. Der Dateiname entspricht dem Methodennamen, die Endung lautet .html.
WWW
Rechnen Prinzipiell ist mit den mathematischen Methoden und den arithmetischen Operatoren im Zusammenspiel natürlich jede Art von mathematischer Berechnung möglich.
1
Ein einfaches Beispiel zeigt ein mögliches Einsatzgebiet. Ein HTML-Formular nimmt in zwei Textfeldern zwei Werte auf. Klickt der Nutzer auf eine Schaltfläche, potenziert eine Funktion den ersten Wert (x) mit dem zweiten Wert (y).
2
Listing 6.1:
3
Eine Potenz ausrechnen (rechnen.html)
Rechnen x y Ergebnis
4
5
6 7
8
9 Abbildung 6.2: Dieses Formular rechnet eine beliebige Potenz aus, die der Nutzer eingibt.
10
11
12
13
183
Objekte in JavaScript
INFO
Natürlich ließe sich dieses Beispiel noch ein wenig ausbauen. Zum einen können Sie weitere Berechnungen wie Wurzelziehen usw. hinzufügen, zum anderen könnten Sie kontrollieren, ob in die Felder auch tatsächlich korrekte Zahlenwerte eingetragen wurden. Programmierer vergessen beim Math-Objekt häufig gerne, dass das »M« am Anfang großgeschrieben werden muss. Mit einer solchen Fehlermeldung reagiert der Browser immer, wenn er ein bestimmtes Objekt nicht kennt. Im Firefox sehen Sie den Fehler in der JavaScript-Fehlerkonsole (Menü EXTRAS/JAVASCRIPT-KONSOLE, siehe Kapitel 30).
Abbildung 6.3: Die Fehlermeldung lässt nicht lange auf sich warten.
Bogenmaß Für die trigonometrischen Methoden sin(Zahl), cos(Zahl) und tan(Zahl) erfolgt die Angabe des Zahlenwerts im Bogenmaß. arcsin(Zahl), arccos(Zahl) und arctan(Zahl) liefern dagegen das Ergebnis im Bogenmaß. Daher ist es notwendig, zu wissen, was ein Bogenmaß ist und wie es berechnet wird. Das Bogenmaß bezeichnet die Länge des Kreisbogens, die ein Winkel aus einem Kreis herausschneidet (siehe Abbildung 6.4). Der Kreis ist dabei der normierte Einheitskreis mit einem Umfang 2 * π. Abbildung 6.4: Das Bogenmaß für einen 90°-Winkel
184
Objekte in JavaScript
Die Umrechnung ins Bogenmaß erfolgt nach folgender Formel: Bogenmaß = π * Winkel / 180
Die Rückumwandlung sieht entsprechend so aus: Winkel = 180 * Bogenmaß / π
Wem die Umrechnung von Hand zu umständlich ist, der verwendet am besten JavaScript. Ein kleiner Umrechner ist schnell geschrieben: ■
1
Den Anfang machen zwei Textfelder – je eines für das Bogenmaß und eines für den Winkel in Grad.
2
Bogenmaß Winkel
■
■
Der Event-Handler onchange ruft in beiden Textfeldern jeweils die Umrechnungsfunktion auf, wenn der Nutzer einen Wert eingegeben und damit das Textfeld geändert hat. In der Umrechnungsfunktion für den Winkel wird der Wert des Textfelds (angegeben im Bogenmaß) in die Formel zur Umrechnung eingesetzt. Das Ergebnis weist die Funktion direkt dem anderen Textfeld zu.
3
function winkel(feld_bm) { bogen.wi.value = 180 * feld_bm.value / Math.PI; }
5
Die Funktion bogenmass() ist das direkte Gegenstück zu winkel(). Sie nimmt den Wert als Winkel aus dem Textfeld, das sie bei einer Änderung aufruft. Dieser Winkel wird dann in die Umrechnungsformel eingefügt. Das Ergebnis ist ein Bogenmaß, das sofort in das Textfeld für das Bogenmaß eingetragen wird.
6
4
7
function bogenmass(feld_wi) { bogen.bm.value = Math.PI * feld_wi.value / 180; }
8
Nachfolgend der komplette Code: Listing 6.2:
9
Ein Umrechner von Bogenmaß in Winkel und umgekehrt (bogenmass.html)
Bogenmaß Bogenmaß Winkel
10
11
12
13
185
Objekte in JavaScript
Abbildung 6.5: Die Umrechnung von Bogenmaß in einen Winkel funktioniert gewissermaßen »on the fly«.
Runden Das Runden ist sehr einfach. Das folgende Skript rundet eine Zahl, die der Nutzer in ein Textfeld eingibt. Das Ergebnis landet in einem anderen Textfeld: Listing 6.3:
Einfaches Runden (runden.html)
Runden Zu rundende Zahl Ergebnis
Abbildung 6.6: JavaScript rundet die Zahl.
186
Objekte in JavaScript
Bei negativen Zahlen wird –4,50 auf –4 aufgerundet und erst –4,51 auf –5 abgerundet. Vorsicht, das wird häufig falsch gemacht. INFO
Nachkommastellen runden Wollen Sie eine Zahl in den Nachkommastellen runden, müssen Sie den Dezimalpunkt erst um so viele Stellen nach links verschieben, wie Sie runden möchten.
1
In wenigen Schritten lässt sich Listing 6.3 so anpassen, dass auf zwei Stellen hinter dem Komma gerundet wird:
2
■ ■
In der Funktion kommt eine neue Variable als Zwischenspeicher zum Einsatz. Dies erhöht die Übersichtlichkeit. Der Variablen wird als Wert das gerundete Ergebnis der um zwei Stellen nach links verschobenen Zahl übergeben. Für die Verschiebung multipliziert das Skript einfach mit 100.
3
4
var r = Math.round(feld.value * 100); ■
Nun muss die Verschiebung um zwei Stellen nach links rückgängig gemacht werden. Dazu dient die Division durch 100.
5
r = r / 100; ■
Abschließend erhält das zweite Textfeld mit Namen erg den gerundeten Wert zugewiesen.
6
document.formular.erg.value = r;
Der vollständige Code sieht wie folgt aus:
7
Listing 6.4: Nachkommastellen runden (runden_nachkomma.html) Nachkommastellen runden Zu rundende Zahl Ergebnis
8
9
10
11
12
13
187
Objekte in JavaScript
Abbildung 6.7: Das Rundungsskript rundet nun auf zwei Stellen hinter dem Komma.
TIPP
Ändern Sie einfach den Multiplikator, je nachdem, auf wie viele Stellen Sie runden möchten. Wollen Sie auf drei Stellen hinter dem Komma runden, multiplizieren Sie beispielsweise mit 1.000. Soll bereits vor dem Dezimalpunkt gerundet werden, teilen Sie durch ein Vielfaches von 10. max() und min() Math.max() und Math.min() wurden in neueren Browsern verbessert. Sie können nun auch beliebig viele Zahlen miteinander vergleichen und die größte beziehungsweise kleinste herausfinden.
Tabelle 6.4: Math.max() und Math.min()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
max
min
Das folgende Beispiel gibt bei einem Vergleich aus drei Zahlen die größte aus. Zwei der Zahlen werden noch in der max()-Methode mit arithmetischen Operatoren (siehe Abschnitt 3.1 »Operatoren«) aus Variablenwerten errechnet: Listing 6.5:
max() mit mehreren Zahlen (max_neu.html)
max() mit mehreren Zahlen
188
Objekte in JavaScript
Abbildung 6.8: Das richtige Ergebnis ist die dritte Zahl (a * 2 = 6).
1
2
Zufallszahlen Eine Zufallszahl ist schnell erzeugt.
3
Listing 6.6: Eine Zufallsdatei ausgeben (zufall.html) Zufall
4
5
6 Abbildung 6.9: Eine Zufallszahl
7
8
9
10 Beliebige Zufallszahlen Die Zahlen von 0 bis 1 – dazu noch in einer Genauigkeit von 17 Stellen – sind zwar schön, für die meisten Fälle aber nicht ausreichend. Nehmen wir an, Sie brauchen die Zufallszahlen in einer beliebigen Spanne von x bis y, dann hilft folgende Formel:
11
12
x + Math.floor(Math.random() * (y – x + 1))
Sollen die Zufallszahlen nur ganze Zahlen sein, müssen Sie das Ergebnis nur noch runden. Im folgenden Beispiel verwenden wir eine Zufallszahl, um damit Bilder mit den Namen 1 bis 6 zufällig aufzurufen. Diese Bilder zeigen verschiedene Augenzahlen eines Würfels.
13
189
Objekte in JavaScript
WWW
Für das Beispiel finden Sie die benötigten Bilddateien mit den Augenzahlen des Würfels. Sie befinden sich im Ordner code\kap6 und heißen 1.jpg bis 6.jpg. Listing 6.7:
JavaScript simuliert einen Würfel (wuerfel.html).
Würfeln
Abbildung 6.10: Der Würfel zeigt eine 2.
6.1.2
Date
Das Date-Objekt muss – im Gegensatz zu Math – immer mit new instanziiert werden. var datum = new Date();
Diese Zeile weist der Variablen datum das Date-Objekt und damit das aktuelle Datum und die aktuelle Zeit zu. Folgendes Skript gibt das Datum sofort aus: Listing 6.8: Das Datum ausgeben (date.html). Datum
190
Objekte in JavaScript
1 Abbildung 6.11: Das Datum in seiner ursprünglichen Form
2
3
4 Das aktuelle Datum und die aktuelle Zeit beziehen sich bei clientseitigem JavaScript natürlich immer auf den Client, das heißt den Rechner des Nutzers. Wenn der seine Zeit umgestellt hat2 oder in einer anderen Zeitzone lebt, erhält er auch eine andere Uhrzeit.
5 HALT
6
Der lange Datumsstring aus Abbildung 6.11 ist nur in seltenen Fällen praktisch. Meist benötigen Sie einzelne Elemente des Datums. Diese Elemente erhalten Sie mit Methoden des Date-Objekts. Die Methode getDate() liefert beispielsweise den Tag des Monats. Dazu wird einer Variablen der Rückgabewert der Methode zugewiesen.
7
8
Listing 6.9: Eine Methode von Date verwenden (getDate.html) Tag des Monats
9
10
11
12
13 2
Dies ist über das Betriebssystem einfach möglich. Unter Windows ist ein Doppelklick auf die Uhr rechts unten ausreichend. Die Uhrzeit lässt sich im Textfeld ändern. Diese Funktion kann aber deaktiviert sein, wenn der Nutzer kein Administrator ist. Ebenso lässt sich die Uhrzeit in den meisten LinuxFenstermanagern ändern. Am Mac befindet sich die Uhrzeit meist rechts oben.
191
Objekte in JavaScript
Abbildung 6.12: getDate() gibt den Tag im Monat aus.
Das Date-Objekt und die hier genannten Methoden sind in allen neueren Browsern vorhanden. Nur Netscape 2 und 3 besitzen manche Methoden nicht. Tabelle 6.5: Das Date-Objekt Date
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Einige neuere Methoden finden Sie weiter unten im Abschnitt »Neue und spezielle Methoden«. REF
Die meistverwendeten Methoden des Date-Objekts dienen dazu, einen Bestandteil des Datums oder der Uhrzeit zu liefern. Sie beginnen – wie getDate() aus dem vorhergehenden Beispiel – mit get. Wie Sie in Abschnitt »Eigene Daten« erfahren werden, können Sie allerdings auch eigene Daten erstellen. Entsprechend gibt es zu jeder get-Methode eine Methode mit set, die den jeweiligen Datumsbestandteil festlegt. datum.setDate(13);
setzt den Tag im Monat also auf den Dreizehnten. In Tabelle 6.6 finden Sie eine Beschreibung des Inhalts der jeweiligen getund set-Methoden. Denken Sie daran, dass get liefert und set festlegt. Tabelle 6.6: Die Methoden des Date-Objekts
192
Methode (get)
Methode (set)
Beschreibung
getDate()
setDate(Wert)
Tag des Monats von 1 bis 31
getDay()
setDay(Wert)
Tag der Woche von 0 (Sonntag) bis 6 (Samstag)
getFullYear()
setFullYear(Wert)
Jahr mit vier Stellen
getHours()
setHours(Wert)
Stunden des Tages von 0 bis 23
getMilliseconds()
setMilliseconds(Wert)
Millisekunden seit dem 1.1.1970, 00:00:00 GMT (siehe Abschnitt »Mit Daten rechnen, Seite 203«)
Objekte in JavaScript
Tabelle 6.6: Die Methoden des Date-Objekts (Forts.)
Methode (get)
Methode (set)
Beschreibung
getMinutes()
setMinutes(Wert)
Die Minuten der Stunde von 0 bis 59
getMonth()
setMonth(Wert)
Der Monat des Jahres von 0 bis 11
getSeconds()
setSeconds(Wert)
Die Sekunden der Minute von 0 bis 59
getTime()
setTime(Wert)
Die Zeit in Millisekunden seit dem 1.1.1970, 00:00:00 GMT
2
liefert die Differenz der auf dem Client eingestellten Zeitzone zur GMT (Greenwich Mean Time). Für den deutschsprachigen Raum: +60 Minuten
3
getTimezoneOffset()
1
getUTCDate()
setUTCDate(Wert)
liefert den Tag des Monats nach UTC-Zeitzone (UTC entspricht GMT)
getUTCDay()
setUTCDay(Wert)
Tag der Woche von 0 (Sonntag) bis 6 (Samstag) nach UTC
getUTCFullYear()
setUTCFullYear(Wert)
Jahr mit vier Stellen nach UTC
5
getUTCHours()
setUTCHours(We rt)
Stunde des Tages von 0 bis 23 nach UTC
6
getUTCMilliseconds()
setUTCMilliseconds(Wert)
Millisekunden seit dem 1.1.1970, 00:00:00 GMT nach UTC
getUTCMinutes()
setUTCMinutes(Wert)
Minuten einer Stunde von 0 bis 59 nach UTC
getUTCMonth()
setUTCMonth(Wert)
Monat des Jahres von 0 bis 11 nach UTC
getUTCSeconds()
setUTCSeconds(Wert)
Sekunden einer Minute von 0 bis 59 nach UTC
9
getYear()
setYear(Wert)
Jahr als zweistelliger Wert
10
Andere Methoden
Beschreibung
parse(Datum)
wandelt ein Datum in die Zahl der Millisekunden seit 1.1.1970, 00:00:00 GMT um. Wird ohne Date-Objekt eingesetzt:
4
7
8
11
Date.parse(Datum) toGMTString()
liefert einen String mit dem GMT-Datum. Aussehen und Inhalt sind abhängig vom Browser.
toString()
wandelt Datum und Zeit auf dem Rechner des Nutzers in einen String um.
toUTCString()
wandelt Datum und Zeit in einen String nach UTC um.
12
13
193
Objekte in JavaScript
Tabelle 6.6: Die Methoden des Date-Objekts (Forts.)
HALT
Methode (get)
Methode (set)
UTC(jahr, monat, tag, stunden, minuten, sekunden, millisekunden)
liefert die Zahl der Millisekunden zwischen dem 1.1.1970, 00:00:00 UTC und dem als Parameter übergebenen Datum. Die drei Datenkomponenten am Anfang sind verpflichtend, die Zeitkomponenten optional.
Beschreibung
valueOf()
liefert das Datum als String nach UTC.
Achten Sie bei den Datenwerten darauf, dass einige Werte, beispielsweise Tage der Woche (getDay()) oder Stunden des Tages (getHours()), bei 0 zu zählen beginnen. Im Abschnitt »Sommerzeit« finden Sie beschrieben, wie sich diese Werte in normale Daten umformatieren lassen. GMT und UTC Bei Daten ist immer von GMT und UTC die Rede. Was bedeutet dies eigentlich? GMT steht für Greenwich Mean Time, die nach dem Londoner Stadtteil Greenwich benannt ist und die Zeitzone mit der Standardzeit darstellt. Alle Zeitzonen östlich davon haben eine positive Zeitverschiebung, sind also Stunden voraus, alle westlich davon haben eine negative Zeitverschiebung. Der deutschsprachige Raum hat beispielsweise eine positive Zeitverschiebung um eine Stunde: +1 GMT.
Abbildung 6.13: Die Zeitzone unter Windows XP
Die UTC-Zeit (Universal Time Coordinated) entspricht der GMT. UTC ist nur die neuere Bezeichnung.
194
Objekte in JavaScript
Zeitverschiebung Die Zeitverschiebung auf dem lokalen Rechner herauszufinden ist einfach; dies erledigt die Methode getTimezoneOffset(): Listing 6.10: Zeitverschiebung mit getTimezoneOffset() (gettimezoneoffset.html) Zeitverschiebung
1
2
3
4 Abbildung 6.14: -60?
5
6 7 Aber Moment, das Ergebnis ist nicht so berauschend. –60 Minuten soll die Zeitverschiebung betragen (siehe Abbildung 6.14). Im deutschsprachigen Raum ist es aber doch +1 Stunde gegenüber UTC? Das Problem ist folgendes: Da die Methode die UTC intern von der lokalen Zeit abzieht, entsteht bei positiven Werten ein negativer Wert.
8
9
Die Lösung ist nicht schwierig. Die Zeitverschiebung erhält einfach einen Vorzeichenwechsel. Des Weiteren werden die Minuten noch in Stunden umgerechnet:
10
var datum = new Date(); var ver = -(datum.getTimezoneOffset()) / 60; document.write(ver + " UTC");
11 Abbildung 6.15: Nun sieht das Ergebnis wie erwartet aus.
12
13
195
Objekte in JavaScript
Zeitverschiebung simulieren Die getTimezoneOffset()-Methode hat eine Schwäche. Sie kann nicht gesetzt werden. Wollen Sie also beispielsweise Stunde und Minute von New York und von Berlin ausgeben, erfordert dies etwas mehr Aufwand: Listing 6.11: Zeitverschiebung manuell ausrechnen (zeitverschiebung.html) Zeitverschiebung = 5) { st -= 5; } else { st = 24 + st - 5; } document.write(st + ":" + mi + " New York" + ""); st = datum.getUTCHours(); mi = datum.getUTCMinutes(); if (st < 23) { st += 1; } else { st = st - 24 + 1; } document.write(st + ":" + mi + " Berlin"); //-->
Abbildung 6.16: Die Zeitverschiebung ausrechnen
Aber es geht auch etwas einfacher. Die folgende Schleife gibt alle Uhrzeiten in allen Zeitverschiebungen aus. Dies funktioniert wie folgt: ■
Zuerst wird ein neues Date-Objekt mit dem aktuellen Datum instanziiert. var datum = new Date();
196
Objekte in JavaScript
■
In einer zweiten Variablen aktuell speichert das Skript die aktuelle Zeit in Millisekunden seit dem 1.1.1970, 00:00 GMT. Die Zeit in Millisekunden ist der Schlüssel zur Lösung des Umrechnungsproblems. Denn mit den Millisekunden zu einer festen Basis können Sie mit Daten rechnen (siehe Abschnitt »Mit Daten rechnen«)! var aktuell = datum.getTime();
■
1
Die dritte Variable nimmt die Zeitzonenverschiebung der aktuellen Zeit in Minuten auf. var zone = datum.getTimezoneOffset();
■
2
Das Herzstück des kleinen Programms ist die for-Schleife. Die Zählervariable läuft über die ganzen Zeitzonen von –12 bis +12. for (var i = -12; i < 13; i++) {
■
3
In der Schleife wird zuerst die Zeitzone (in Millisekunden3) von der aktuellen Zeit abgezogen. Anschließend wird die Zeitverschiebung (gespeichert in der Zählervariablen i) ebenfalls in Millisekunden4 dazugezählt.
4
var erg = aktuell + zone * 60 * 1000 + i * 60 * 60 * 1000; ■
Ein neues Date-Objekt erhält den Zeitwert in Millisekunden. Daraus wird automatisch ein neues Datum gebildet.
5
var time = new Date(erg); ■
Als Nächstes geben wir Teile des Datums, hier Stunden und Minuten, aus.
6
document.write(time.getHours() + ":" + time.getMinutes()); document.write(" " + i + " GMT");
7
}
Nachfolgend der vollständige Code: Listing 6.12: Die Zeitverschiebung in allen Zeitzonen (zeitverschiebung_einfach.html)
8
Zeitverschiebung 3 4
9
10
11
12
13
Die Umrechnung von Minuten in Millisekunden lautet: Minuten * 60 * 1.000 = Millisekunden. Die Umrechnung von Stunden in Millisekunden lautet: Stunden * 60 * 60 * 1.000 = Millisekunden.
197
Objekte in JavaScript
Abbildung 6.17: Die aktuelle Uhrzeit in allen Zeitzonen
Eigene Daten Im letzten Beispiel (siehe Listing 6.12) gibt bereits ein Betrag in Millisekunden das Datum für ein neues Date-Objekt vor. Die Syntax dazu sieht folgendermaßen aus: var datum = new Date(Millisekunden);
Das Datum wird aus den Millisekunden seit dem 1.1.1970, 00:005 GMT errechnet. var datum = new Date(264290400000);
entspricht beispielsweise dem 18. April 1978. Sie haben aber noch eine andere Möglichkeit, ein eigenes Datum anzugeben. Zuerst wieder die Syntax: var datum = new Date(Jahr, Monat, Tag, Stunden, Minuten, Sekunden, Millisekunden);
5
198
Dieser Zeitpunkt wurde als Beginn der Unix-Epoche festgelegt. Das Datum in Millisekunden heißt seitdem auch Unix-Zeitstempel.
Objekte in JavaScript
Die Angaben von Jahr, Monat und Tag sind hier obligatorisch. Das Jahr muss vierstellig angegeben werden, der Monat von 0 (Januar) bis 11 (Dezember) und der Tag von 1 bis 31. Optional sind die Angaben der Zeit. Wenn sie erfolgen, dann allerdings in der festgesetzten Reihenfolge. Sie können also nicht nur Sekunden angeben, aber keine Minuten und Stunden.
1
Die Stunden werden von 0 (Mitternacht) bis 23 angegeben, die Minuten von 0 bis 59, die Sekunden ebenfalls von 0 bis 59 und die Millisekunden von 0 bis 999.
2
var datum = new Date(1978, 3, 18); var datum2 = new Date(2004, 6, 3, 9, 30, 0);
3
In der oberen Zeile wird nur das Datum übergeben, die Zeit wird automatisch auf 00:00:00 Uhr gestellt. In der unteren Zeile ist auch eine Zeitangabe bis hin zu Sekunden, aber ohne Millisekunden vorhanden.
4 Abbildung 6.18: Ohne Uhrzeitangabe (erste Zeile des Codes) stellt der Interpreter die Uhrzeit auf 00:00:00.
5
6 7
Das Jahr-2000-Problem
8
Das Jahr-2000-Problem6 ist eigentlich bereits abgehakt. War ja alles nicht so schlimm … Das stimmt zwar, allerdings gibt es in JavaScript ein paar Überbleibsel. In JavaScript gibt es die Methoden getYear() und setYear(). Sie arbeiten mit einer zweistelligen Jahreszahl.
9
Das folgende Beispiel zeigt die Probleme:
10
Listing 6.13: getYear() hat ein Jahr-2000-Problem (jahr2000.html). Jahr 2000
Der Internet Explorer behilft sich auch in der aktuellen Version 7 damit, alle Jahre über 2000 vierstellig auszugeben (siehe Abbildung 6.19). Abbildung 6.19: Der Internet Explorer 7 gibt das Jahr 2004 vierstellig aus.
Im Firefox funktioniert diese Krücke nicht mehr (siehe Abbildung 6.20). Abbildung 6.20: Das Jahr 2004 verkommt zu 104.
Den besten Ausweg bei diesen Problemen bietet die Methode getFullYear(), die das Jahr vierstellig zurückgibt. In sehr alten Browsern, Netscape 2 und 3 und Internet Explorer 3 in der ersten Version, ist diese Methode allerdings noch nicht enthalten. In diesem Fall hilft nur eine Fallunterscheidung, die Sie am einfachsten in eine Funktion packen: Listing 6.14: Jahr-2000-Fallunterscheidung (jahr2000_faelle.html) Jahr 2000 Fallunterscheidung 100 && jahr < 200) { return jahr + 1900; } else { return jahr; } }
1
var datum = new Date(1978, 4, 18); document.write(getJahr2000(datum) + ""); var datum2 = new Date(2002, 3, 4); document.write(getJahr2000(datum2)); //-->
2
3
Die Fallunterscheidung hat nur einen Haken. Liegt das Jahr zwischen 100 und 200, weil Sie beispielsweise über die römische Kaiserzeit schreiben, funktioniert die Fallunterscheidung nicht mehr.
4 Abbildung 6.21: Auch Netscape 2 kommt jetzt mit Jahr-2000-Daten zurecht.
5
6 7
8
9 Wenn Sie die if-Bedingung verändern, können Sie natürlich auch die Jahre vor 2000 vierstellig schreiben. Entfernen Sie dazu einfach die Bedingung jahr > 100. Wollen Sie die Jahre ab 2000 mit zweistelliger Endung7 angezeigt bekommen, ziehen Sie einfach 100 ab, statt 1900 dazuzuzählen. Allerdings müssen Sie noch einen zusätzlichen Fall für die neueren Internet Explorer (im Listing hervorgehoben) einfügen, da sie das Jahr vierstellig ausgeben:
10
11
function getJahr2000(datum) { var jahr = datum.getYear(); if (jahr > 100 && jahr < 200) { return jahr - 100; } else if (jahr > 1999){ 7
12
13
Hier besteht natürlich Verwechslungsgefahr mit den Jahren 1900, 1901 usw. Andererseits wird eine zweistellige Jahreszahl häufig bei der Bezeichnung von Jahrgängen oder aktuellen Dokumenten wie Auftragsbestätigungen und Bestellungen benötigt. Sie automatisch in JavaScript zu generieren, ist also durchaus öfter notwendig.
201
Objekte in JavaScript
return jahr - 2000; } else { return jahr; } }
Soll das Jahr auch bei einstelligen Ziffern zweistellig sein, müssen Sie noch einen String hinzufügen. Dafür muss wieder eine if-Bedingung herhalten: Listing 6.15: Jahreszahlen zweistellig und IE-kompatibel (zweistellig_ie.html) function getJahr2000(datum) { var jahr = datum.getYear(); if (jahr > 100 && jahr < 200) { jahr -= 100; if (jahr < 10) { return "0" + jahr; } else { return jahr; } } else if (jahr > 1999){ jahr -= 2000; if (jahr < 10) { return "0" + jahr; } else { return jahr; } } else { return jahr; } }
Abbildung 6.22: Auch im IE klappt es jetzt mit den zweistelligen Jahreszahlen.
Daten aus der Vergangenheit Sehr alte Browser unterstützen keine Daten vor dem 1.1.1970, 00:00:00 GMT. Dazu gehören der Netscape Navigator 2 und 3 sowie der Internet Explorer 3 in der ersten Version. Bei diesen Browsern können Sie ältere Daten also nicht als Date-Objekt speichern.
202
Objekte in JavaScript
Mit Daten rechnen Um mit Daten zu rechnen, müssen Sie sie immer in Millisekunden seit der Unix-Epoche, also seit dem ominösen 1.1.1970, 00:00:00 GMT, umrechnen. Dann funktioniert die Rechnung aber sehr einfach: ■
Zuerst instanziieren Sie zwei Date-Objekte. var datum1 = new Date(1978, 3, 18); var datum2 = new Date(2004, 6, 3);
■
1
Anschließend ziehen Sie die mit getTime() in Millisekunden seit dem 1.1.1970 umgewandelten Datenwerte voneinander ab.
2
var erg = datum2.getTime() - datum1.getTime(); ■
Da das Ergebnis noch in Millisekunden vorliegt, wird es in Tage umgerechnet und abgerundet, damit ganze Tage herauskommen:
3
erg = Math.floor(erg / (1000 * 60 * 60 * 24)); ■
Abschließend gibt das Skript das Ergebnis aus:
4
Listing 6.16: Mit Daten rechnen (datum_rechnen.html) document.write(erg + " Tage"); Mit Datumswerten rechnen
5
6 7
8
9 Abbildung 6.23: Zwischen den zwei Daten liegen einige Tage.
10
11
12
13
203
Objekte in JavaScript
Wenn Sie bei zwei Datenwerten nicht wissen, welcher aktueller und damit größer und welcher älter (kleiner) ist, könnten Sie sich mit einer if-Fallunterscheidung behelfen. Noch einfacher geht es allerdings mit den mathematischen Methoden Math.max() und Math.min(). Damit finden Sie zuerst heraus, welcher Datumswert (in Millisekunden) größer und welcher kleiner ist. Anschließend wird der kleinere Wert vom größeren abgezogen. Listing 6.17: Dieses Skript findet automatisch heraus, welches Datum aktueller ist (datum_vergleich.html). var datum1 = new Date(1978, 4, 18); var datum2 = new Date(2001, 12, 6); var gr = Math.max(datum1.getTime(), datum2.getTime()); var kl = Math.min(datum1.getTime(), datum2.getTime()); var erg = gr - kl; erg = Math.floor(erg / (1000 * 60 * 60 * 24)); document.write(erg + " Tage");
INFO
Ähnlich nützlich wie die Methoden zum Auslesen (getTime(), getDate() etc.) sind die Methoden zum Setzen von Zeit und Datum (setTime(), setDate() etc.). Damit können Sie ein Datum sehr gezielt steuern und verändern. Sommerzeit Die Sommerzeit lässt sich leicht einrechnen. Addieren Sie eine Stunde hinzu. Am einfachsten erfolgt dies wieder in Millisekunden: zeit + 60 * 60 * 1000
Auf die Uhrzeit reagieren Wenn eine Webseite immer gleich aussieht, besucht der Nutzer sie sehr wahrscheinlich kein drittes Mal. Das Wichtigste, um Nutzer dauerhaft zu binden, ist natürlich der Inhalt. Aber auch optische Veränderungen können sehr reizvoll sein. Beispielsweise könnten Sie spontan auf die Uhrzeit reagieren. Das folgende Skript ändert das Hintergrundbild, je nachdem, ob es gerade Tag oder Nacht ist. Die Uhrzeit wird aus einem Date-Objekt mit getHours() ausgelesen. Eine Fallunterscheidung überprüft, ob es nach 7 und vor 20 Uhr noch Tag ist. In diesem Fall wird das Hintergrundbild tag.jpg verwendet, ansonsten (im else-Fall) das Hintergrundbild nacht.jpg. Die Beispielbilder zum Skript finden Sie im Download-Archiv im Verzeichnis code\kap06. Sie heißen, wie im Skript zu sehen, tag.jpg und nacht.jpg. WWW
204
Objekte in JavaScript
Listing 6.18: Das Skript ändert den Hintergrund je nach Uhrzeit (uhrzeit.html).8 Auf die Uhrzeit reagieren 7 && datum.getHours() < 20) { document.body.background = "tag.jpg"; } else { document.body.background = "nacht.jpg"; } } //-->
1
2
3
4 Abbildung 6.24: Dieser Screenshot ist von 12 Uhr mittags.
5
6 7
8
9
10
11 Dieselbe Idee können Sie natürlich auch verwenden, um beispielsweise täglich verschiedene Bilder einzusetzen oder im Wochenrhythmus Bilder zu wechseln.
12 TIPP
13 8
Dieses Skript funktioniert nicht im Netscape Navigator 4.x. Mehr dazu erfahren Sie in Kapitel 22 »CSS und JavaScript«.
205
Objekte in JavaScript
Abbildung 6.25: Die Sonne ist untergegangen. Ein Screenshot, aufgenommen um 21 Uhr
Uhrzeit aktualisieren Natürlich soll die Uhrzeit immer aktuell sein. Um dies zu realisieren, verwendet das folgende Beispielskript zwei neue Möglichkeiten des Browsers. ■
Die Funktion window.setInterval("Befehl", Millisekunden) ruft einen Befehl oder eine Funktion immer wieder auf, und zwar in regelmäßigen Abständen, die unter Millisekunden angegeben werden. var aufruf = window.setInterval("uhrzeit()", 500);
INFO
Das Gegenstück zu setInterval() ist clearInterval(ID). Diese Methode hebt ein Intervall wieder auf. Die ID ist der Variablenname, bei dem setInterval() aufgerufen wurde. Im vorhergehenden Beispiel heißt diese ID also aufruf. clearInterval(aufruf); ■
Die Ausgabe erfolgt nicht mit document.write(), sondern mit einem -Block, der über getElementById() angesprochen wird, da nur so eine stets aktuelle HTML-Ausgabe in einer Seite möglich ist. function uhrzeit() { var uhr = new Date(); document.getElementById("block").innerHTML = uhr.getHours() + ":" + uhr.getMinutes() + ":" + uhr.getSeconds(); }
206
Objekte in JavaScript
getElementById() ist eine Möglichkeit, über das DOM auf Elemente zurückzugreifen, funktioniert allerdings nicht in alten Browsern. Wollen Sie noch Netscape Navigator 4.x und Internet Explorer 4 unterstützen, benötigen Sie andere Lösungen. Mehr dazu erfahren Sie in Kapitel 8 »Document Object Model«.
INFO
1
Nachfolgend der vollständige Code: Listing 6.19: Die Uhrzeit laufend aktualisieren (uhrzeit_aktualisieren.html).
2
Die Uhrzeit aktualisieren
3
4
5
6 Abbildung 6.26: Stets die aktuelle Uhrzeit auf der Seite.
7
8
9
10 Wollen Sie statt der Clientzeit die Serverzeit, können Sie diese natürlich per serverseitiger Technologie (PHP, ASP.NET etc.) einfügen. Allerdings verlieren Sie dann den Vorteil der regelmäßigen Aktualisierung ohne Neuladen der Seite. Wollen Sie diesen behalten, verwenden Sie einen Ajax-Aufruf, der in regelmäßigen Abständen die Uhrzeit vom Server holt.
11 TIPP
12
Countdown 13
Ähnlich wird auch ein Countdown realisiert. Hier wird nur innerhalb der Funktion das aktuelle Datum vom angestrebten Zieldatum abgezogen. Wenn Sie es wünschen, können Sie noch eine etwas kompliziertere
207
Objekte in JavaScript
Umrechnung hinzufügen, die den zurückgegebenen Betrag von Millisekunden in Tage, Stunden, Minuten und Sekunden umrechnet: Listing 6.20: Ein Counter bis 1.1.2003 (counter.html) Counter
Abbildung 6.27: Der Countdown zählt die Tage, Stunden, Minuten und Sekunden bis zum 1.1.2003 rückwärts.
Datum formatieren Die Daten in JavaScript werden – von den meisten Browsern – in englischer Form ausgegeben. Das heißt, nicht nur die Reihenfolge von Tag, Monat und Jahr ist ungewöhnlich, sondern auch die Monats- und Tagesnamen passen nicht. Die Umformatierung des Datums ist allerdings nicht sehr aufwändig, wenn Sie die Methoden verwenden, um Elemente wie den Monat (getMonth()) einzeln auszugeben. 208
Objekte in JavaScript
Ein Beispiel zeigt dies. Ziel ist es, das Datum in normaler Form mit deutschem Monatsnamen auszugeben: 28. November 2002
Tag und Jahr sind kein Problem. Den Tag des Monats erhalten Sie mit getDate(), das vierstellige Jahr mit getFullYear(). 1
Problematischer ist der Monat. getMonth() liefert den Monat als Zahl zwischen 0 (Januar) und 11 (Dezember). Für die Umwandlung stehen zwei mögliche Wege zur Verfügung: ■ ■
2
Eine switch- oder if-Fallunterscheidung, die alle Zahlen von 0 bis 11 durchprüft und entsprechend den Monatsnamen zurückliefert Ein Array mit den Monatsnamen
3
Die zweite Lösung erscheint hier praktikabler, weil sie etwas einfacher und damit weniger fehleranfällig eingegeben werden kann. Das Beispiel verwendet Arrays, die in Kapitel 7 »Arrays und Strings« detaillierter besprochen werden.
4
Das Array besteht aus den zwölf Monatsnamen. Die Indexnummern im Array entsprechen jeweils den Monatsnummern, die von getMonth() ausgegeben werden.
5
6
Listing 6.21: Das Datum im gewünschten Format ausgeben (datum_formatieren.html) Datum formatieren
13
209
Objekte in JavaScript
INFO
Die Umformatierung von Daten ist auf manchen Websites eine sehr häufige Aufgabe. Hier lohnt es sich, die Umwandlung in eine Funktion oder eine Objektmethode zu packen und sie in einer externen JavaScript-Datei zur Verfügung zu stellen. Wir haben in Kapitel 5 »OOP« gezeigt, wie Sie das DateObjekt um eine solche Methode erweitern können.
Abbildung 6.28: Das Datum im manuell erstellten Format
Verbinden Sie die Datumsformatierung nun noch mit der Fallunterscheidung aus dem Abschnitt »Das Jahr-2000-Problem«, und Sie erhalten die Jahreszahl in korrekter Form auch für Netscape 2 und 3 und die erste Version des Internet Explorers 3. Listing 6.22: Ausschnitt aus: datum_formatieren_y2k.html function getJahr2000(datum) { var jahr = datum.getYear(); if (jahr < 200) { return jahr + 1900; } else { return jahr; } } . . . var datum = new Date(); . document.write(getJahr2000(datum));
Abbildung 6.29: Mit der Jahr-2000Fallunterscheidung funktioniert das Skript sogar im Netscape Navigator 2.
210
Objekte in JavaScript
Neue und spezielle Methoden Einige Methoden für das Date-Objekt sind erst in neueren Browsern hinzugekommen oder beschränken sich speziell auf den Internet Explorer. Tabelle 6.7 gibt einen Überblick und Tabelle 6.8 zeigt, in welchen Browsern die Methoden funktionieren. Tabelle 6.7: Die neueren und speziellen Methoden des Date-Objekts
1
Methode
Beschreibung
toDateString()
wandelt ein Date-Objekt in einen String um. Enthält nur das Datum, nicht die Zeit. Ist auf den Internet Explorer beschränkt.
toLocaleString()
gibt das Datum und die Uhrzeit als String zurück, wie auf dem lokalen System vorgesehen. Das Ergebnis ist vom Browser abhängig.
toLocaleDateString()
gibt nur das Datum im lokalen Format, abhängig vom Browser, zurück.
3
toLocaleTimeString()
gibt nur die Zeit in einem lokalen Format zurück. Das Aussehen ist vom Browser abhängig.
4
getVarDate()
liefert das Datum in dem Format VT_DATE, das bei der Interaktion mit ActiveXund DOM-Objekten benötigt wird. Es ist auf den Internet Explorer beschränkt.
toTimeString()
liefert nur die Zeit als String.
2
5
6 Im Download-Archiv finden Sie im Ordner code\kap06 für jede Methode ein Beispiel. Der Dateiname entspricht jeweils dem Namen der Methode. WWW NS4.x M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
toDateString()
toLocaleString()
toLocaleDateString()
toLocaleTimeString()
8
9
10
getVarDate() toTimeString
7
Tabelle 6.8: Neue Methoden von Date
11
Prinzipiell ist insbesondere der Einsatz der lokalen Datums- und Zeitstrings durchaus sinnvoll. Der Internet Explorer 7 und der Firefox liefern sogar deutsche Tages- und Monatsnamen (siehe Abbildung 6.30 und Abbildung 6.31). Allerdings herrscht hier keine Einigkeit unter den Browsern. Also bleibt dem Entwickler nichts anderes übrig, als das Datum selbst zu formatieren.
12
13
211
Objekte in JavaScript
Abbildung 6.30: Der Internet Explorer 7 verwendet deutsche Tagesund Monatsnamen.
Abbildung 6.31: Der Firefox kann ebenfalls Deutsch.
Vorgefertigte Datumsobjekte Das Browserobjekt document besitzt drei Eigenschaften, die Datumsformate zurückliefern: ■ ■ ■
Tabelle 6.9: Eigenschaften mit vorgefertigten Datumsobjekten
212
lastModified – Zeitpunkt, zu dem das Dokument zuletzt gespeichert wurde fileCreatedDate – Zeitpunkt, zu dem das Dokument erstellt wurde. Beschränkt auf den Internet Explorer fileModifiedDate – ebenfalls der Zeitpunkt, zu dem das Dokument zuletzt bearbeitet wurde, allerdings auf den Internet Explorer beschränkt NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
fileCreatedDate
fileModifiedDate
lastModified
Objekte in JavaScript
Da die drei Eigenschaften auf Informationen vom Server angewiesen sind, können die Ergebnisse sehr unpräzise sein. Diesen Funktionen sollten Sie daher nicht unbedingt trauen.
INFO
Ein einfaches Beispiel gibt das Datum aus, an dem das aktuelle Dokument zuletzt geändert wurde.
1
Listing 6.23: Wann wurde die Datei zuletzt geändert (dateiinfo.html)? Dateiinfo
6.1.3
2
3
4
5
6
Boolean
Wahrheitswerte tauchen in der Programmierung an allen Ecken und Enden auf. Normalerweise müssen Sie sich darum nicht kümmern. Zusätzlich gibt es in JavaScript allerdings noch ein Boolean-Objekt. Dieses Objekt wird ebenfalls mit dem Konstruktor new instanziiert. Als Parameter übergeben Sie einen Wahrheitswert:
7
8
var wahr = new Boolean(true);
9
Dieses Objekt besitzt zwei Methoden: ■
■
toString() – liefert einen String, der entweder "true" oder "false" enthält. Wenn Sie einen Wahrheitswert ausgeben, erfolgt diese Umwandlung automatisch. valueOf() – gibt den Wert – also true oder false – des Boolean-Objekts zurück.
10
11
Das Boolean-Objekt und seine Methoden gibt es seit Netscape Navigator 3 und der zweiten Version des Internet Explorers 3, also seit JavaScript 1.1. 12 Sie können das Boolean-Objekt wie alle JavaScript-eigenen Objekte um Ihre eigenen Methoden erweitern. Mehr dazu erfahren Sie in Kapitel 5 »OOP«. 13
TIPP
213
Objekte in JavaScript
6.1.4
Number
Auch Zahlen werden, wie Wahrheitswerte, in JavaScript grundsätzlich einfach verwendet. Das Number-Objekt besitzt allerdings einige zusätzliche Eigenschaften und Methoden. Ein Number-Objekt wird wie folgt instanziiert: var zahl = new Number(25);
In Tabelle 6.10 sehen Sie die Eigenschaften des Number-Objekts. Tabelle 6.10: Die Eigenschaften des NumberObjekts
Eigenschaft
Beschreibung
MAX_VALUE
Die größte mögliche Zahl in JavaScript
MIN_VALUE
Die kleinste mögliche Zahl in JavaScript
NaN
Alle Objekte, die keine Zahl sind. Dies geschieht vor allem beim Rechnen mit Strings oder bei der Umwandlung von Strings in Zahlen. Sie testen mit der globalen Funktion isNaN(), ob es sich um den Datentyp NaN handelt (siehe Kapitel 4 »Funktionen«).
POSITIVE_INFINITY
Positive Unendlichkeit (8); alle Zahlen, die größer als die größte mögliche Zahl in JavaScript sind
NEGATIVE_INFINITY
Negative Unendlichkeit (– 8); alle Zahlen, die kleiner als die kleinste mögliche Zahl in JavaScript sind
Das Number-Objekt ist in JavaScript bereits eine lange Zeit enthalten. Nur Netscape 2 und der Internet Explorer 3 in Version 1 bleiben außen vor. Die Eigenschaften sind ebenfalls seit dieser Zeit integriert. Die Methoden sind teilweise neuer. Die folgende Tabelle gibt Ihnen eine Übersicht: Tabelle 6.11: Die Methoden des Number-Objekts
214
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
toExponential()
toFixed()
toLocaleString()
toPrecission()
toString()
valueOf()
Objekte in JavaScript
Methode
Beschreibung
toExponential(Stellen)
wandelt das Number-Objekt in Exponentialschreibweisea um und gibt es als String zurück. Als Parameter können Sie die Zahl der Dezimalstellen angeben.
toFixed(Stellen)
wandelt eine Zahl in die Dezimalzahlschreibweise um. Die Zahl der Nachkommastellen wird dabei exakt festgelegt. Falls nötig, rundet der Interpreter. Dafür wird nur die Stelle als Referenz genommen, die genau hinter der erlaubten Stellenzahl liegt. Bei 44.3248 und einer Stellenzahl von 2 also die 4. Die Rückgabe erfolgt als String.
toLocaleString()
Tabelle 6.12: Die Methoden des Number-Objekts
1
konvertiert die Zahl in das Standardformat auf dem lokalen System. Dies ist von Browser zu Browser unterschiedlich. Die Rückgabe erfolgt als String.
2
77.777 wird im Internet Explorer 6 beispielsweise als 77.78 ausgegeben, im Netscape Navigator 7 dagegen als 77.777.
toPrecision(Stellen)
gibt eine Zahl mit der exakten Stellenzahl an, und zwar auch vor dem Dezimalpunkt.
3
34.678 wird bei einer Präzision von 2 Stellen zu 35. Eine Präzision von 4 Stellen ergibt 34.68. Auch bei dieser Funktion wird die Stelle nach der angestrebten Stellenzahl zum Runden verwendet. Stellen danach werden entfernt. Die Rückgabe erfolgt als String.
toString()
wandelt eine Zahl in einen String um.
valueOf()
liefert den Wert eines Number-Objekts.
a.
4
5
Zur Exponentialschreibweise siehe Kapitel 2 »Syntax und Variablen«.
6 Ein einfaches Beispiel steht hier stellvertretend für die übrigen Methoden des Number-Objekts. Der Code gibt mit der Methode toExponential() die Zahl 700 in Exponentialschreibweise aus:
7
Listing 6.24: toExponential() (toexponential.html) toExponential
8
9
10
11 Abbildung 6.32: 700 in Exponentialschreibweise
12
13
215
Objekte in JavaScript
6.1.5
Global
Das Objekt Global ist ein merkwürdiger Geselle. Es ist eine Art Überobjekt. Alle Variablen und Funktionen, die Sie deklarieren, sind Teil des GlobalObjekts. Da es aber selbst nicht in Erscheinung tritt, brauchen Sie sich nicht darum zu kümmern. Wichtig ist nur, dass aufgrund dieses Objekts Variablen und Funktionen im gesamten Skript zur Verfügung stehen.
6.1.6
Error
Das Error-Objekt ist noch recht neu. Es dient zur Fehlerkontrolle. Sie können damit beispielsweise eigene Ausnahmen als Fehler definieren und entsprechend darauf reagieren. Tabelle 6.13: Das Error-Objekt
NS4.x
Error
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Mehr zum Fehlerhandling erfahren Sie in Kapitel 29»Debugging«. REF
6.1.7
Proprietäre Objekte
Neben den allgemein verfügbaren Objekten gibt es auch browserspezifische Objekte. Sie sind allerdings nicht mit den Browserobjekten zu verwechseln, die Zugriff auf das Browserfenster und seinen Inhalt bieten. Die browserspezifischen Objekte bieten spezielle Funktionalitäten, die meist nur einer der großen Browser besitzt. ActiveX-Objekte (nicht nur von Windows) Der Internet Explorer unter Windows unterstützt einige Objekte, die JavaScript die Arbeit mit dem Betriebssystem erlauben sollen. Der Kontakt funktioniert hier hauptsächlich über so genannte ActiveX-Objekte. Da diese Objekte auf den Internet Explorer und noch enger auf Windows beschränkt sind und weniger JavaScript- (bzw. JScript-), sondern eher ActiveX-Programmierung erfordern, finden Sie in diesem Buch nur eine kurze Beschreibung. Folgende Objekte stehen zur Verfügung: ■ ■
216
ActiveXObject – dient zum Zugriff und zur Verwaltung der Internet Explorer-ActiveX-Objekte Dictionary – speichert Werte mit ihrem Schlüssel. Da JavaScript assoziative Arrays unterstützt, die dasselbe können, ist dieses Objekt eigentlich überflüssig und wurde wohl hauptsächlich für VBScript-Programmierer eingeführt.
Objekte in JavaScript
■
■
■
Enumeration – erlaubt den Zugriff auf Kollektionen aus verschiedenen Eigenschaften. Diese Eigenschaften können beispielsweise die Eigenschaften eines Browserobjekts sein. Da es in JavaScript allerdings den Direktzugriff über das DOM gibt, ist auch dieses Objekt in der Praxis kaum in Gebrauch. FileSystemObject – erlaubt den Zugriff auf das Dateisystem. Dateien können geschrieben, geöffnet und gespeichert werden. Allerdings ist dies abhängig von den Sicherheitseinstellungen des Internet Explorers. VBArray – gestattet den Zugriff auf Visual Basic-Arrays, die nur gelesen werden können. Diese Arrays sind meist Rückgabewerte eines ActiveXObjekts.
Der Gedanke liegt nahe, diese praktische Funktionalität in einem Intranet zu verwenden, das auf Windows-Maschinen mit IE basiert. Das Problem besteht jedoch darin, dass die Sicherheitseinstellungen im IE recht lax sein müssen, damit der Zugriff auf das Dateisystem erlaubt ist. Da die Rechner im Intranet aber meistens auch am Internet hängen, damit die Mitarbeiter dort surfen können, erscheint dies vielen Administratoren als nicht empfehlenswert.
1
2
3 TIPP
4
5
6 7
8
9
10
11
12
13
217
Inhalt
7
Arrays und Strings 1
Dieses Kapitel ist zwei der wichtigsten Datentypen gewidmet: Arrays und Strings. Arrays sind Sammlungen von Daten und daher bereits ein »fortgeschrittenes« Programmierkonstrukt. Aber was machen die bereits zur Genüge bekannten Strings hier? »Zur Genüge bekannt« stimmt nur halb. In diesem Kapitel finden Sie das String-Objekt und erfahren, wie Sie Teile eines Strings ausschneiden, eine Zeichenkette in Strings suchen und String-Formatierungen ändern.
2
7.1
5
3
4
Arrays
Ein Array speichert beliebig viele Daten.1 Diese Daten werden im Array nacheinander angeordnet. Sie können sich dies wie eine Perlenschnur vorstellen. Damit Daten auch wieder auffindbar sind, werden sie mit einem Index versehen. Der Index eines Arrays beginnt bei 0. Das erste Element hat also den Index 0, das zweite den Index 1 usw.
6
7
Ein Array ist in JavaScript ein Objekt, daher wird es mit dem Konstruktor new instanziiert.
8
var Arrayname = new Array();
Das Array-Objekt steht in JavaScript schon immer zur Verfügung. Nur im Netscape 2 und im Internet Explorer 3 in der ersten Version wird ein Array nicht als Objekt angesehen. Daher sind die Eigenschaften und Methoden nicht vorhanden. Erstellen und auslesen können Sie ein Array allerdings auch dort.
Array
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
9
10 Tabelle 7.1: Das Array-Objekt
11
12
Neue Elemente fügen Sie mit dem Array-Namen und dem Index hinzu: Arrayname[0] = Wert;
Diese Zeile weist also dem ersten Array-Element einen Wert zu. Der Index steht immer in eckigen Klammern hinter dem Array-Namen. Das folgende Ab einer gewissen Datenmenge leidet beim Eingeben und Auslesen die Performance. Dies ist allerdings in der Praxis kaum relevant.
219
ndex
1
13
Arrays und Strings
kleine Beispiel erzeugt ein Array und füllt danach die ersten drei Array-Elemente mit Strings: var namen = new Array(); namen[0] = "Carolin"; namen[1] = "Christine"; namen[2] = "Phillip";
Wenn Sie die Daten erst einmal im Array gespeichert haben, benötigen Sie natürlich Zugriff darauf. Eines der Elemente im Array sprechen Sie wie folgt an: Arrayname[Index]
Soll also aus dem Beispiel-Array der Name »Christine« ausgegeben werden, ist die folgende Zeile ausreichend: document.write(namen[1]);
Abbildung 7.1: Der Browser zeigt das richtige Element aus dem Array.
7.1.1
Alternativen und Kurzformen
Die soeben gezeigte Möglichkeit, ein Array zu definieren und zu füllen, ist nicht die einzige. Als Parameter für das Array-Objekt können Sie auch gleich zu Beginn die Länge des Arrays angeben: var Arrayname = new Array(Laenge);
Wenn Sie ein Array ohne Länge erzeugen, hat es keine Elemente. Geben Sie dagegen am Anfang eine Länge an, wird gleich die entsprechende Anzahl an Elementen reserviert. Jedes der Elemente erhält den Wert undefined. Die Eigenschaft length (siehe Abschnitt 7.1.3) des Array-Attributs erhält in diesem Fall den Wert, den Sie im Array-Objekt als Parameter angegeben haben.
HALT
Die Angabe der Länge als einziger Parameter funktioniert in sehr alten Browsern, namentlich Netscape 2 und Internet Explorer 3 in der ersten Version, nicht. Statt der Länge können Sie als Parameter für das Array-Objekt auch gleich die Array-Elemente eintragen: var Arrayname = new Array(Element1, Element2, …);
220
Arrays
In dieser Variante werden die Indexnummern automatisch zugewiesen. Element1 hat also Index 0, Element2 Index 1 usw. Die dritte Kurzform kommt ganz ohne new-Konstruktor und Array-Objekt aus, steht dafür aber in eckigen Klammern: var Arrayname = [Element1, Element2, Element3, …];
1
Diese Variante heißt auch Array-Literal, da der Variablen direkt ein Literal mit dem Array zugewiesen wird. Diese Art der Notation von Arrays wird in Ajax-Applikationen häufig verwendet und ist ein Bestandteil von JSON (JavaScript Object Notation). Das Array-Literal steht im Gegensatz zum Array-Objekt im Netscape Navigator 3 noch nicht zur Verfügung. Sie benötigen Browser ab Internet Explorer 4 und Netscape Navigator 4.x.
2
3 INFO
4
Datentypen mischen In einem Array können Sie die Datentypen beliebig mischen.
5
var mischen = new Array(1, "text", true);
Andere Programmiersprachen wie beispielsweise Java erlauben dies in dieser Form nicht. Da JavaScript aber nur schwach typisiert, also beispielsweise eine automatische Typänderung möglich ist, gibt es bei Datentypen in Arrays keine Beschränkungen.
6 INFO
7 Arrays verschachteln Wenn beliebige Datentypen in einem Array speicherbar sind, warum dann nicht auch mehrere Arrays?2 Folgender Code erstellt ein Array und weist es dann einem neuen Array als einzelnes Element zu:
8
var namen = new Array(); namen[0] = "Carolin"; namen[1] = "Christine"; namen[2] = "Phillip"; var alle = new Array(namen, "Katrin");
9
10
Eine alternative Definitionsmethode von verschachtelten Arrays erfolgt mit eckigen Klammern: var alle = [["Carolin", "Christine", "Phillip"], "Katrin"];
11 INFO
Der Zugriff auf das Element erfolgt wie gewöhnlich. Geben Sie es beispielsweise direkt aus:
12
document.write(alle[0]);
13
Mit dieser Zeile geben Sie das komplette erste Array aus (siehe Abbildung 7.2).
2
Verschachtelte Arrays heißen auch mehrdimensionale oder multidimensionale Arrays.
221
Arrays und Strings
Abbildung 7.2: Das Array liefert alle drei Elemente.
Dies ist häufig nicht der gewünschte Effekt. Daher dürfen Sie ein Element im verschachtelten Array auch direkt ansprechen. Dazu folgt der Indexwert des zweiten Arrays in eckigen Klammern nach dem ersten: document.write(alle[0][1]);
Abbildung 7.3: Der doppelte Index gibt nur noch einen Namen zurück.
Wie tief Sie ein Array verschachteln, bleibt Ihnen überlassen. Auch eine dreifache Verschachtelung ist möglich, wie das folgende Beispiel zeigt. Allerdings wird es hier bereits sehr unübersichtlich. Listing 7.1:
Drei Arrays ineinander verschachteln (array_verschachteln_drei.html)
Arrays verschachteln
222
Arrays
Die Ausgabe bleibt das zweite Element aus dem Array namen (siehe Abbildung 7.4). Abbildung 7.4: Der zweite Eintrag aus dem ersten Array
1
2
3 Was hier mit Strings eher eine Lernübung ist, kann bei der Programmierung durchaus praktisch sein. Denken Sie nur an Zahlenmatrizen, die Sie mit mehrdimensionalen Arrays miteinander verknüpfen können.
4 TIPP
5
7.1.2
Elemente ändern
Um Elemente in einem Array zu ändern, ist es ausreichend, dem Index einen anderen Wert zuzuweisen. Folgender Code ändert das dritte Element des Arrays von »Phillip« in »Steffi«:
6
var namen = new Array("Carolin", "Christine", "Phillip"); namen[2] = "Steffi";
7
Bei Änderungen darf selbstverständlich auch der Datentyp wechseln.
7.1.3
8
Die Länge eines Arrays
Die length-Eigenschaft enthält die Länge eines Arrays, also die Zahl der Elemente. Die Abfrage erfolgt sehr einfach in Punktnotation:
9
var namen = new Array("Carolin","Christine","Phillip"); document.write(namen.length);
10 Abbildung 7.5: Das Array besteht aus drei Elementen.
11
12
13
223
Arrays und Strings
Zur Berechnung der Länge des Arrays verwendet die Eigenschaft length intern das letzte Array-Element und addiert 1. Sind dazwischen Array-Elemente leer geblieben, wird dies nicht berücksichtigt. Listing 7.2:
Probleme mit der length-Eigenschaft (array_laenge_problem.html)
Probleme mit length
Dieses Listing gibt 4 als Länge aus, obwohl der Index 1 kein Element besitzt.
7.1.4
for-in- und andere Schleifen
Der for-in-Schleife wurde bisher in Kapitel 3 »Programmieren« nur wenig Platz eingeräumt. Das liegt nicht daran, dass sie so unwichtig wäre, sondern an ihrem speziellen Anwendungsgebiet. Die for-in-Schleife dient hauptsächlich dazu, Elemente aus Arrays und Eigenschaften von Objekten auszulesen. Die Syntax zeigt die Schlüsselrolle des Operators in: for (Variable in Array) { Anweisungen; }
Die Schleife wird so lange durchlaufen, wie sich Elemente im Array befinden. Bei jedem Schleifendurchlauf wird der Index des aktuellen Elements in der Variablen gespeichert. Wie nützlich dies ist, zeigt das folgende Beispiel: Listing 7.3:
Ein Array mit for-in auslesen (array_forin.html)
var namen = new Array("Carolin","Christine","Phillip"); for(var i in namen) { document.write("Index " + i + ": "); document.write(namen[i] + ""); }
Hier prüft die for-in-Schleife das Array namen. Solange Elemente darin enthalten sind, wird der Variablen i der Index des soeben durchlaufenen Objekts übergeben. In den Schleifenanweisungen greift die Ausgabe mit dem Index auf die Array-Elemente zu.
224
Arrays
Abbildung 7.6: Der Index und die Array-Elemente erscheinen im Browser.
1
2 Andere Schleifen Als Alternative zur for-in-Schleife verwenden Sie einfach eine andere Schleifenart und überprüfen in der Bedingung die Länge des Arrays. Achten Sie aber darauf, dass die Länge immer um 1 höher ist als der Index.
3
Das folgende Beispiel verwendet eine gewöhnliche for-Schleife:
4
Listing 7.4:
Ein Array mit for auslesen (array_for.html)
5
Array mit for
6
7 8
9
Das Beispiel erzeugt dieselbe Ausgabe, wie es in Abbildung 7.6 zu sehen ist. Achten Sie darauf, dass die Ausgabe mit der Prüfung von length auch die undefinierten Elemente mitliefert, wenn in einem Array nicht alle Elemente definiert sind. Listing 7.5:
10
Die for-Ausgabe mit dem bekannten length-Problem (for_length_problem.html)
11
var namen = new Array(); namen[0] = "Carolin"; namen[2] = "Christine"; namen[3] = "Phillip"; for(var i = 0; i < namen.length; i++ ) { document.write("Index " + i + ": "); document.write(namen[i] + ""); }
12
13
Der Code aus Listing 7.5 liefert also für Index 1 den Wert undefined.
225
Arrays und Strings
Abbildung 7.7: Index 1 ist undefined
Eine for-in-Schleife vermeidet dies, da sie undefinierte Werte ignoriert: Listing 7.6:
Die for-in-Schleife gibt nur definierte Elemente aus (forin_problem.html).
for(var i in namen) { document.write("Index " + i + ": "); document.write(namen[i] + ""); }
Abbildung 7.8: Der Index 1 wird nicht ausgegeben.
7.1.5
Array-Elemente löschen
Um ein Array-Element zu löschen, ist es ausreichend, ihm einen leeren String oder den Datentyp null zuzuweisen. Dabei gibt es aber ein Problem: Das Element wird auch in einer for-in-Schleife ausgegeben. Es ist also noch als Array-Element vorhanden. Ab Netscape Navigator 4 und Internet Explorer 4 bietet JavaScript allerdings eine Alternative. Sie können mit dem Schlüsselwort delete einzelne ArrayElemente löschen. Das folgende Beispiel illustriert dies. Zuerst wird das Element mit Index 0 gelöscht, anschließend werden in einer for-in-Schleife alle übrig gebliebenen Elemente ausgegeben: Listing 7.7:
Mit delete Array-Elemente entfernen (delete.html)
Mit delete Array-Elemente löschen
226
Arrays
1
2 Abbildung 7.9: Der Eintrag bei Index 0 wurde entfernt.
3
4
5
6
7.1.6
Assoziative Objekte
7
Ein assoziatives Objekt ist ein Objekt, das statt eines numerischen Index einen Index mit Namen besitzt. Der Name hat den Datentyp String und muss daher in Anführungszeichen stehen. Listing 7.8:
8
Ein assoziatives Objekt (assoziativ.html)
var namen = new Array(); namen["Carolin"] = 18; namen["Christine"] = 15; namen["Phillip"] = 13;
9
10 Alternativ zu new Array() können Sie zum Instanziieren auch new Object() verwenden. Allerdings wird ein assoziativer Array in JavaScript immer als Objekt und nicht als Array behandelt.
INFO
11
Für assoziative Objekte gibt es auch eine Kurzform, die ebenfalls Bestandteil von JSON ist:
12
var namen = { "Carolin": 18, "Christine":15, "Phillip":13 }
13
227
Arrays und Strings
Die Elemente in einem assoziativen Objekt werden immer mit ihrem Indexnamen angesprochen, also beispielsweise: namen["Phillip"]
Natürlich können Sie auch mit einer for-in-Schleife auf das assoziative Array zugreifen. Die Syntax gleicht der bei einem gewöhnlichen Array: for(var i in namen) { document.write("Index " + i + ": "); document.write(namen[i] + ""); }
Abbildung 7.10: Der Index ist eine Zeichenkette; damit werden die Elemente ausgegeben.
In der Praxis dienen assoziative Arrays hauptsächlich dazu, Schlüssel/DatenPaare zu speichern. INFO
Länge Assoziative Arrays haben eine Schwäche: Die length-Eigenschaft berücksichtigt assoziative Elemente nicht. Daher können Sie assoziative Elemente nur mit der for-in-Schleife auslesen und nicht, wie bei gewöhnlichen Arrays (siehe Listing 7.4), mit einer anderen Schleife. Folgendes Beispiel zeigt dies. Die Ausgabe der Länge ergibt nur den Wert 1, da die drei assoziativen Array-Elemente ignoriert werden. Listing 7.9:
Assoziative Elemente zählen nicht zur Länge des Arrays (assoziativ_length.html).
Assoziatives Array
228
Arrays
Abbildung 7.11: Das Array hat nur eine Länge von 1.
1
2
7.1.7
Methoden für Arrays 3
Das Array-Objekt bietet einige Methoden, um mit Arrays zu arbeiten. Darunter befinden sich Methoden, um Arrays zu sortieren, zu verbinden oder die Reihenfolge umzudrehen. Da die Methoden in der Praxis bedeutsam sind, finden Sie sie in den nächsten Abschnitten jeweils mit Beispiel erklärt.
4
Sortieren mit sort()
5
Die Funktion sort() dient dazu, die Elemente eines Arrays zu sortieren. Die Funktion steht ab Netscape 3 und Internet Explorer 3 in der zweiten Version zur Verfügung.
sort()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
6 Tabelle 7.2: sort()
7 8
Die Sortierung erfolgt standardmäßig alphabetisch. Allerdings werden zum Vergleich die ASCI-Codes der einzelnen Zeichen herangezogen. Das bedeutet, dass Kleinbuchstaben immer größer als Großbuchstaben sind.
9 Dieses Problem kennen Sie bereits von den Vergleichsoperatoren in Abschnitt 3.1.3 im Abschnitt »Vergleich von Strings«. 10
REF
Bei Zahlenwerten erfolgt die Sortierung mit sort() ebenfalls über die ASCIIWerte. Zahlen werden also in Strings umgewandelt. Bei diesem System ist 9 größer als 15, da immer zuerst das erste Zeichen verglichen wird. Ist das erste Zeichen also schon unterschiedlich, steht die Sortierreihenfolge fest. In der Praxis ist die Sortierung von Zahlenwerten mit sort() daher nicht verwendbar. Alternativ können Sie für die sort()-Methode eine Sortierfunktion definieren.
11
12 TIPP
13
229
Arrays und Strings
Ein Wahrheitswert true oder false erfährt für die Sortierung ebenfalls eine Umwandlung in einen String. Anschließend wird also der String »true« oder »false« mit den anderen Array-Elementen als Strings verglichen. Auch dies hat in der Praxis meist keinen Wert. Einsetzbar ist die Methode sort() allein also am besten für Strings, die beispielsweise einheitlich mit Groß- oder Kleinschreibung beginnen. Der folgende Beispielcode sortiert ein Array mit drei Namen in alphabetischer Reihenfolge: Listing 7.10: Mit sort() arbeiten (sort.html) Sort()
Abbildung 7.12: Die Array-Elemente haben die Reihenfolge und damit auch den Index getauscht.
HALT
230
Ein assoziatives Array können Sie nicht sortieren, da die Elemente keine festgelegte Reihenfolge haben (im Download-Archiv zu finden unter code\ kap07\sort_assoziativ.html).
Arrays
Sortieren mit Funktion sort() kann als optionalen Parameter eine Funktion übernehmen: sort(Funktion)
Die Funktion enthält Anweisungen für die Sortierung. Wie funktioniert das? sort() schickt nacheinander jedes mögliche Paar aus zwei Array-Elementen an diese Funktion. Die Funktion muss diese zwei Elemente miteinander vergleichen. Wenn das erste Element größer als das zweite ist, gibt die Funktion einen negativen Wert zurück, ist es kleiner, einen positiven. Aus diesem Wert erkennt sort() also, welches der beiden Elemente größer ist. Hat die Methode alle Werte für alle möglichen Zweierpaarungen von Elementen, kann daraus auf die Rangfolge geschlossen werden.
1
2
3
Ein Beispiel zeigt dies. Das Array besteht aus drei Zahlenwerten: var zahlen = new Array(); zahlen[0] = 15; zahlen[1] = 42; zahlen[2] = 9; ■
4
Die Sortierung erfolgt mit sort(). Als Parameter wird die Funktion sort_funk aufgerufen, die die Sortieranweisungen enthält.
5
zahlen.sort(sort_funk); ■
6
Die Sortierfunktion übernimmt die zwei Parameter. Sie erinnern sich? Die sort()-Methode übergibt zwei der Array-Elemente immer als zwei Parameter. Diese Parameter werden voneinander subtrahiert und das Ergebnis ist der Rückgabewert. Er ist negativ, wenn y größer als x ist, und positiv, wenn x größer als y ist.
7
function sort_funk(x,y) { return x - y; } ■
8
Zuletzt gibt eine for-in-Schleife die Array-Elemente aus, um zu testen, wie sich die Sortierung ausgewirkt hat.
9
for(var i in zahlen) { document.write("Index " + i + ": "); document.write(zahlen[i] + ""); }
10
Nachfolgend das vollständige Beispiel: 11
Listing 7.11: Eine Sortierfunktion einsetzen (zahlen.html) Sort() mit Funktion
Abbildung 7.13: Die Zahlen sind numerisch sortiert.
INFO
Ein verbreiteter Irrtum ist, dass der Rückgabewert der Funktion +1 oder –1 sein müsste. Ein positiver oder negativer Wert ist ausreichend, wie das vorangegangene Beispiel zeigt. Häufig wird nur +1 oder –1 als vorgegebener Rückgabewert verwendet. concat() Die Methode concat() verbindet zwei Arrays miteinander. Die Methode ist in der dritten Generation der Netscape- und Microsoft-Browser noch nicht integriert, in späteren Browsern aber verfügbar.
Tabelle 7.3: concat() concat()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Die Syntax von concat() ist einfach: var Array_gesamt = Array1.concat(Array2);
Dabei wird Array2 an Array1 angehängt.
232
Arrays
Natürlich kann Array_gesamt auch Array1 oder Array2 sein. In einem der beiden Arrays können also die Elemente beider zusammengefasst werden. Array1 = Array1.concat(Array2);
INFO
Das folgende Beispiel macht aus zwei Arrays mit Namen ein Array, das alle Namen enthält.
1
Listing 7.12: Der Einsatz von concat() (concat.html) concat()
5
6
Abbildung 7.14: Die zwei Arrays sind miteinander in einem verbunden.
7 8
9
10
11 join()
12
join("Trennzeichen") gibt alle Elemente eines Arrays hintereinander als String zurück. Als Trennzeichen können Sie einen beliebigen String angeben.
13
233
Arrays und Strings
join() ist in den gängigen Browsern vorhanden, nur Netscape 2 und Internet Explorer 3 in der ersten Version (JavaScript 1.0) unterstützen es nicht, da sie auch das Array-Objekt nicht unterstützen. Tabelle 7.4: join() join()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel zeigt, dass sogar der Einsatz von HTML-Tags möglich ist: Listing 7.13: join() (join.html) join()
Abbildung 7.15: Die Elemente des Arrays mit Strichpunkten und Zeilenumbruch getrennt
INFO
Das Ausgangs-Array wird von der Methode join() nicht geändert. Wird der Rückgabewert von join() einer Variablen zugewiesen, hat diese Variable den Datentyp String und keine Verbindung mehr zum Original-Array. reverse() Die Methode reverse() dreht die Reihenfolge der Elemente in einem Array um.3 reverse() ist wie join() in den gängigen Browsern vorhanden. 3
234
In einem assoziativen Array macht diese Methode keinen Sinn, da assoziative Indexnamen keine Reihenfolge vorgeben.
Arrays
NS4.x
reverse()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 7.5: reverse()
Das folgende Skript sortiert die Namen aus dem Array in umgekehrter Reihenfolge:
1
Listing 7.14: reverse() im Einsatz (reverse.html) reverse()
5
6
7 Abbildung 7.16: Die Elemente im Array haben ihre Plätze getauscht.
8
9
10
slice()
11
Die Methode slice() schneidet Elemente aus einem Array heraus und bildet daraus ein neues Array. Dabei wird als erster, verpflichtender Parameter das Startelement angegeben, ab dem ausgeschnitten wird.
12
var Array_neu = Array.slice(2);
Die vorhergehende Zeile liefert ein neues Array, das alle Elemente des alten Arrays ab Indexnummer 2 besitzt. Als zweiten optionalen Parameter können Sie ein Element angeben, bei dem das Ausschneiden endet. Dieses Element wird nicht mehr ausgeschnitten.
13
235
Arrays und Strings
var Array_neu = Array.slice(2,3);
Diese Codezeile schneidet also nur das Element 2 aus dem alten Array aus und weist es dem neuen Array zu. slice() ist nicht in der dritten Generation des Internet Explorers und Netscape Navigators enthalten, dafür aber in allen neueren Versionen, wie der folgenden Tabelle zu entnehmen ist: Tabelle 7.6: slice() slice()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel schneidet zwei Elemente aus einem Array aus und übergibt sie an ein neues Array (mit Namen neu), das anschließend ausgegeben wird: Listing 7.15: Die Methode slice() (slice.html) slice()
Abbildung 7.17: Zwei Elemente aus dem alten Array wurden in das neue Array übernommen.
236
Arrays
Mit assoziativen Arrays funktioniert die Methode slice() nicht, da hier Start- und Endposition nicht als Indexwert definierbar sind. Negative Werte für den zweiten Parameter wie bei String.slice() sind ebenfalls nicht möglich.
HALT
slice() mit einer Funktion einfügen
1
Dass slice() nicht mit dem Netscape Navigator 3 funktioniert, ist meist unproblematisch. Wünschen Sie dagegen die größtmögliche Abwärtskompatibilität, können Sie slice() auch durch eine Funktion ersetzen, die dieselben Aufgaben bewerkstelligt:
2
Listing 7.16: Statt slice() eine selbst geschriebene Funktion verwenden (slice_funktion.html)
3
9
10
11
12
13
237
Arrays und Strings
Abbildung 7.18: Mit einer Funktion lassen sich Elemente aus einem Array auch im Netscape 3 auslesen.
splice() Die splice()-Methode ist recht mächtig. Sie entfernt Elemente aus der Mitte eines Arrays und kann sie einem neuen Array zuweisen. splice() ist im Internet Explorer erst seit Version 5.5 enthalten. Der Navigator hat diese Methode seit Version 4.x. Tabelle 7.7: splice() splice()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Im Folgenden sehen Sie die Syntax: var Array_neu = Array.splice(Start, Elementzahl, Ersatz1, Ersatz2, …);
splice() löscht ab dem bei Start angegebenen Index so viele Elemente, wie die Elementzahl vorgibt. Diese beiden Parameter sind verpflichtend, die weiteren Parameter nur optional. Sie erhalten jeweils einen Datensatz als Ersatz für die herausgelöschten Daten. Sind weniger Ersatzdaten als gelöschte Daten vorhanden, werden die Daten ab Start ersetzt und die letzten nur gelöscht.
TIPP
Wenn Sie mehr Daten einsetzen als herauslöschen, werden diese Daten hinzugefügt. Schalten Sie die zu löschende Elementzahl auf 0, fügt der JavaScript-Interpreter sogar nur Daten ein. Das folgende Beispiel löscht nur den Index Nummer 1, da als Elementzahl nur 1 angegeben ist: Listing 7.17: splice() in Aktion (splice.html) splice()
1
+ i + ": "); "");
2
+ j + ": "); + "");
3
4
Abbildung 7.19: Das neue Array hat als Index 0 das Element aus dem alten Array. Im alten Array fehlt das Element.
5
6
7 8
push() und pop()
9
push(Wert) fügt einen Wert am Ende des Arrays hinzu, pop() entfernt das letzte Element des Arrays. Dabei müssen Sie nicht wissen, welche Indexposition das letzte Element besitzt.
10
push() und pop() sind im Internet Explorer erst seit Version 5.5 enthalten. Der Navigator besitzt sie seit Version 4.x.
11
NS4.x
M/FF
push()
pop()
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 7.8: push() und pop()
12
13
239
Arrays und Strings
Das folgende Beispiel zeigt push() und pop() im Einsatz: ■
Ein HTML-Formular besteht aus einem großen Textbereich (), in dem das Skript den Inhalt des Arrays ausgibt, einem Textfeld für neue Namen und zwei Schaltflächen. Die erste Schaltfläche dient dazu, neue Elemente an das Array anzufügen. Im onclick-Event-Handler ruft sie die Funktion eintrag() auf. Die zweite Schaltfläche löscht das letzte Element des Arrays. Dazu ruft sie bei onclick die Funktion loeschen() auf.
■
Der -Teil enthält zuerst das Array: hier das bekannte Beispiel mit Namen. var namen = new Array(); namen[0] = "Carolin"; namen[1] = "Christine"; namen[2] = "Phillip"; namen[3] = "Konstantin";
■
Anschließend folgt die Funktion ausgabe(), die von setInterval jede Sekunde (1.000 Millisekunden = 1 Sekunde) aufgerufen wird.4 Die Funktion gibt mit einer for-in-Schleife alle Elemente des Arrays aus. var zeit = setInterval(ausgabe, 1000); function ausgabe() { document.fo.ausgabe.value = ""; for(var j in namen) { document.fo.ausgabe.value+="Index " + j + ": "; document.fo.ausgabe.value+=namen[j] + "\n"; }
Die Funktion wird immer wieder aufgerufen, um neu an das Array angefügte Elemente mit auszugeben. Wenn sie nur am Anfang einmal aufgerufen würde oder wenn der Ausgabecode nicht in einer Funktion stünde, würde ein neuer oder gelöschter Eintrag nicht im mehrzeiligen Textfeld erscheinen.
HALT
Der Konqueror 3 kommt mit diesem Beispiel nicht zurecht, da er mit setInterval() in diesem Kontext Schwierigkeiten hat. Als Alternative können Sie setTimeout() verwenden.
4
240
setInterval() verwendet hier die Funktionsreferenz. Dies erkennen Sie an den fehlenden runden Klammern. Sie könnten alternativ auch den Funktionsnamen direkt verwenden.
Arrays
■
Klickt der Nutzer auf die Schaltfläche EINTRAGEN, ruft er damit wie bereits beschrieben die Funktion eintrag() auf. Diese Funktion fügt mit push() den Wert des einzeiligen Textfelds an das Ende des Arrays an: function eintrag() { namen.push(document.fo.name.value); }
■
Die zweite Schaltfläche LÖSCHEN entfernt das letzte Element des Arrays mit pop(). Dies kann ein neu hinzugefügtes, aber natürlich auch eines der per Skript definierten Elemente sein.
1
function loeschen() { namen.pop(); }
2
3
Das komplette Beispiel sehen Sie hier: Listing 7.18: push() und pop() (push_pop.html) push() und pop()
10
11
12
13
241
Arrays und Strings
TIPP
Da das Skript auf Elemente aus dem Formular direkt zugreift, müssen diese bereits bestehen. Da ein -Bereich im Kopf der HTML-Seite beim Laden gleich ausgeführt wird, kennt es aber unter Umständen5 die Elemente im Körper der Seite noch nicht. Schreiben Sie daher das Skript einfach in den -Bereich.
Abbildung 7.20: Sie können einen Text eintragen ...
Abbildung 7.21: … und der Text wird in das Array übernommen.
HALT
Im Allgemeinen sollten Sie die Funktion setInterval in der Praxis nur einsetzen, wenn Sie sie unbedingt benötigen, da sie im Browser doch einige Ressourcen verbraucht. Sie sehen, dass je nach Rechnerstärke die Anzeige der Array-Inhalte leicht flackert. Dies signalisiert das Neuladen.
5
242
Wie der Ladevorgang abläuft, ist von verschiedenen Faktoren wie Cache, zuerst übertragene Daten der HTML-Seite usw. abhängig. Daher ist eine Prognose hier schwierig.
Arrays
shift() und unshift() Was push(Wert) und pop() für das Ende eines Arrays sind, sind unshift(Wert) und shift() für den Anfang des Arrays. unshift(Wert) fügt einen Wert an der Indexstelle 0 ein, shift() entfernt das Element mit Index 0. unshift() und shift() sind ebenfalls erst ab Internet Explorer 5.5 und ab Netscape Navigator 4.x eingebunden. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
unshift()
shift()
1 Tabelle 7.9: unshift() und shift()
2
3 Listing 7.19 ist schnell umgeschrieben. Die beiden Funktionen eintrag() und loeschen() werden statt mit push(Wert) und pop() mit unshift(Wert) und shift() versehen:
4
Listing 7.19: Elemente am Anfang des Arrays einfügen und entfernen (Ausschnitt aus:
unshift_shift.html)
5
function eintrag() { namen.unshift(document.fo.name.value); } function loeschen() { namen.shift(); }
6
7 Abbildung 7.22: Den neuen Namen fügt das Skript am Anfang ein.
8
9
10
11
12 In Strings umwandeln Die Funktion toString() wandelt ein Array in einen String um. Das Ergebnis ist vorhersehbar. Die Array-Elemente werden durch Kommata getrennt6 ausgegeben. 6
13
Ohne Leerzeichen nach den Kommata!
243
Arrays und Strings
toLocaleString() dagegen richtet sich bei der Darstellung der Array-Elemente nach den lokalen Browsereinstellungen. Die Auswirkungen wird ein kleines Beispiel zeigen. toString()ist wesentlich älter als toLocaleString(). Sie ist bereits im Netscape 3 und Internet Explorer 3 in der zweiten Variante vorhanden, toLocaleString() dagegen nur in den neueren Browsern (siehe Tabelle 7.10). Tabelle 7.10: toString() und toLocaleString()
toString() toLocaleString()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Zum Testen verwendet das folgende Beispiel beide Methoden toString() und toLocaleString(): Listing 7.20: Ein Array in einen String umwandeln (toString_toLocaleString.html) toString() und toLocaleString()
toLocaleString() ist wie bereits erwähnt von den lokalen Einstellungen, das heißt vom Länder- bzw. Sprachcode und vom Browser selbst abhängig. So versieht der deutsche Internet Explorer (5.5, 6 und 7) das Array mit Strichpunkt und Leerzeichen als Trennzeichen zwischen den Elementen (siehe Abbildung 7.23). Firefox, Opera und Konqueror/Safari stellen es genauso dar wie mit toString(), also mit Kommata und ohne Leerzeichen getrennt. Ältere Mozilla-Versionen geben bei Strings und Wahrheitswerten als Array-Elemente nur den Datentyp aus. Zahlen werden dagegen als Zahl ausgegeben.
244
Arrays
Die Umwandlung in Strings, insbesondere mit toLocaleString(), ist in der Praxis nicht sehr zweckmäßig, da die Ergebnisse sehr unterschiedlich sind. Verwenden Sie besser join().
TIPP
Abbildung 7.23: toLocaleString() erscheint im Internet Explorer 7 mit Strichpunkt und Leerzeichen als Trennung.
1
2
3
4
Abbildung 7.24: Der Firefox gibt die Array-Elemente mit Kommata getrennt aus.
5
6
7 8
7.1.8
Alternative Datenspeicherung mit eval() 9
Die globale Funktion eval("String") verwendet einen String und führt ihn als JavaScript-Code aus. Diese Funktion kann auch zur Datenspeicherung eingesetzt werden.
10 Die grundlegenden Erläuterungen zur eval()-Funktion finden Sie in Abschnitt 4.3.3 »eval()«. REF
Eine besondere Gefahr bei eval() ist die Code-Injektion. Dadurch, dass alles in eval() ausgeführt wird, sind z.B. über GET-Parameter feindliche CodeEinschlüsse möglich (siehe auch Kapitel 30 »Sicherheit«).
11
12
TIPP
13
245
Arrays und Strings
Für die Datenspeicherung setzt eval()Variablennamen zusammen. Es geht einfach los: ■
Vier normale Variablen werden deklariert. Beachten Sie die einheitliche Namensgebung. Jede Variable beginnt mit namen und hat dann eine Nummer. var var var var
namen0 namen1 namen2 namen3
= = = =
"Carolin"; "Christine"; "Phillip"; "Konstantin";
Wenn Sie sich dabei an den Index eines Arrays erinnert fühlen, sind Sie auf der richtigen Spur! TIPP
■
Nun folgt eine for-Schleife. Sie wird so oft durchlaufen, wie es vorher Variablen gab (also viermal). for(var i = 0; i
246
Arrays
Abbildung 7.25: Die Ausgabe ist nicht von der eines Arrays zu unterscheiden.
1
2
3 Natürlich ist die eval()-Funktion als Array-Ersatz nicht so praktisch wie ein Array selbst. Sonst wäre sie ja kein Ersatz. Beispielsweise lässt sie sich nicht mit einer for-in-Schleife auslesen und auch die Zahl der Objekte kann nicht über length bestimmt werden.
7.1.9
4
INFO
5
Neuerungen in JavaScript 1.6 und 1.7
In JavaScript 1.6 und 1.7 sind noch einige neue Array-Funktionen hinzugekommen, die auch in anderen Programmiersprachen vorhanden sind: ■ ■ ■
■ ■ ■
6
indexOf(Element) liefert den Index des ersten Vorkommens des angegebenen Elements. lastIndexOf(Element) liefert den Index des letzten Vorkommens des angegebenen Elements. every(Funktion) geht alle Array-Elemente durch und ruft eine Funktion auf. Liefert diese für alle Array-Elemente true zurück, liefert every() auch true. filter(Funktion) führt für alle Array-Elemente eine Funktion aus und liefert einen Array für alle Elemente, für die die Funktion true liefert. forEach(Funktion) führt für jedes Array-Element eine Funktion aus. some(Funktion) geht alle Array-Elemente durch und ruft eine Funktion auf. Liefert diese für mindestens eines der Array-Elemente true zurück, liefert some() auch true.
7 8
9
10
11
Die genannten Funktionen sind bisher nur im Mozilla implementiert. NS4.x
M/FF
indexOf()
lastIndexOf()
every()
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 7.11: Neue ArrayMethoden
12
13
247
Arrays und Strings
Tabelle 7.11: Neue ArrayMethoden (Forts.)
NS4.x
M/FF
filter()
match()
forEach()
some()
7.2
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Strings
Mit Strings haben Sie bis hierher bereits häufig gearbeitet. Jetzt folgen noch ein paar Schmankerl. Das String-Objekt bietet eine Eigenschaft, die StringLänge mit length und einige Methoden, die die Arbeit mit Strings erleichtern. JavaScript unterstützt das String-Objekt seit Version 1.0. Es ist auch in den sehr alten Browsern wie Netscape ab Version 2 und Internet Explorer 3 in beiden Varianten vorhanden. Allerdings enthalten die frühen Versionen bei weitem noch nicht alle Methoden. Dazu finden Sie jedoch bei den einzelnen Methoden Übersichtstabellen. Tabelle 7.12: String-Objekt String
7.2.1
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
String-Länge
Die Eigenschaft length kennen Sie bereits von Arrays. Sie gibt die Länge einer Zeichenkette aus. Die Syntax ist einfach: String.length;
length ist für das String-Objekt bereits in den ersten Browsern Netscape 2 und Internet Explorer 3 in beiden Varianten enthalten. INFO
Tabelle 7.13: length length
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Die folgenden Zeilen geben die Länge des Worts »Nilpferd« aus. Listing 7.22: Die Länge einer Zeichenkette (string_length.html) var text = "Nilpferd"; document.write(text.length);
248
Strings
Abbildung 7.26: »Nilpferd« besteht aus acht Buchstaben.
1
2 Die Eigenschaft length zählt jedes Zeichen zur String-Länge hinzu, auch Leer- und Sonderzeichen. 3
TIPP
7.2.2
Formatieren
Einige Methoden des String-Objekts dienen dazu, Text zu formatieren. Die Formatierung entspricht dabei dem jeweiligen HTML-Tag.
4
Die Formatiermethoden des String-Objekts gelten als nicht mehr empfohlen. Sie sind auch nicht Teil des JavaScript-Standards! Auch das produzierte HTML ist nicht mehr empfohlen (engl.: deprecated).
String
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
5
Tabelle 7.14: Formatiermethoden des String-Objekts
6
7
Der Einsatz der String-Methoden ist sehr einfach: 8
var text = "Nilpferd"; document.write(text.bold());
Diese Zeilen geben den Text »Nilpferd« fett aus. 9
Die folgende Tabelle gibt eine Übersicht über die Formatiermethoden. Methode
Beschreibung
anchor("Name")
legt einen Anker (-Tag) um den Text und vergibt ein name-Attribut.
big()
legt ein -Tag um den String und stellt den Text damit größer dar.
blink()
erzeugt mit dem -Tag einen blinkenden Text. Der Internet Explorer und der Konqueror kennen dieses Tag nicht und ignorieren es daher.
bold()
stellt den Text mit -Tag fett dar.
fixed()
erzeugt diktengleiche Schrift mit -Tags, das heißt Schrift, deren Buchstaben alle gleich breit sind. Diese Schrift verwendet man beispielsweise für Listings. Die bekannteste diktengleiche Schriftart ist Courier.
Tabelle 7.15: Formatiermethoden des String-Objekts
10
11
12
13
249
Arrays und Strings
Methode
Beschreibung
fontcolor(Farbe) gibt die Farbe des Strings als Hexadezimalwert (z.B. #000000 für Schwarz) oder als Farbname (black) an. Der Farbname wird vom Prinzip her in ein -Tag mit color-Attribut geschrieben. fontcolor(Größe) gibt die Größe der Farbe in den sieben aus HTML bekannten Größenstufen von 1 bis 7 an. Entspricht dem size-Attribut des -Tags. italics()
steht für kursive Schreibweise, also den Einschluss in -Tags.
link(URL)
legt ein -Tag um den Text. Das href-Attribut für das Link-Ziel übernimmt den Parameter URL der Methode.
small()
stellt den Text mit -Tags kleiner dar.
strike()
streicht den Text mit -Tags durch.
sub()
stellt den Text tief; entspricht also -Tags.
sup()
stellt den Text hoch; entspricht also -Tags.
Das folgende Beispiel zeigt, wie mehrere Formatierungen auf einen Text angewendet werden: Listing 7.23: Ein Text lässt sich mit mehreren Methoden formatieren (formatieren.html). Formatieren
Abbildung 7.27: Die Schrift ist als Link fett formatiert.
250
Strings
Das Formatieren mit CSS und entsprechenden CSS-Klassen ist wesentlich gängiger als diese Formatiervariante. Details dazu lesen Sie in Kapitel 22 »CSS und JavaScript«.
7.2.3
INFO
Groß- und Kleinbuchstaben 1
Die Methoden toLowerCase() und toUpperCase()7 dienen dazu, Strings in Klein- bzw. Großbuchstaben umzuwandeln. toLowerCase() und toUpperCase() sind bereits in den ersten Browsern Netscape 2 und Internet Explorer 3 in beiden Varianten vorhanden. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
toLowerCase()
toUpperCase()
2
Tabelle 7.16: toLowerCase() und toUpperCase()
Das folgende Beispiel wandelt einen Text zuerst in Klein- und dann in Großbuchstaben um:
3
4
5
Listing 7.24: toLowerCase() und toUpperCase() (toLowerCase_toUpperCase.html)
6
toLowerCase() und toUpperCase()
7 8
9
Abbildung 7.28: Das »NilPferd« in Klein- und Großbuchstaben
10
11
12
13
7
Der Begriff Case kommt vom Setzkasten aus dem Buchdruck. In einzelnen Setzkästen waren die Buchstaben enthalten.
251
Arrays und Strings
Sonderzeichen bleiben von der Umwandlung verschont. INFO
Praxiseinsatz Sie sollten Strings immer dann in Klein- oder Großbuchstaben umwandeln, wenn Sie beispielsweise einen Vergleich durchführen möchten, der alphabetisch unabhängig von Groß- und Kleinschreibung sortiert. Das folgende Beispiel zeigt dies anhand einer Sortierfunktion für ein Array: Listing 7.25: Sortieren mit Kleinbuchstaben (sort_lowerCase.html) sort() mit Kleinbuchstaben y.toLowerCase()) return +1; else return -1; } var namen = new Array(); namen[0] = "arnold"; namen[1] = "Walter"; namen[2] = "Viktor"; namen.sort(sortieren); for(var i in namen) { document.write("Index " + i + ": "); document.write(namen[i] + ""); } //-->
Die Sortierfunktion wandelt die Strings vor dem Vergleich erst in Kleinbuchstaben um, damit die unterschiedlichen ASCII-Codes von Groß- und Kleinbuchstaben keine Rolle mehr spielen. Natürlich wäre alternativ auch eine Umwandlung in Großbuchstaben möglich. Da die Umwandlung nur in der Funktion erfolgt, sind die Originalstrings nicht betroffen. Abbildung 7.29: Die Sortierung funktioniert
252
Strings
Um Strings und numerische Werte gemischt zu sortieren, müssen Sie in die Sortierfunktion eine Fallunterscheidung einbauen, die zwischen Zahlen und Strings unterscheidet.
TIPP
toLocaleLowerCase() und toLocaleUpperCase() toLocaleLowerCase() und toLocaleUpperCase() sind neuere Alternativen zu den Methoden aus dem letzten Abschnitt. Sie beachten auch die lokale Sprache des Clients, da in manchen Sprachen Großbuchstaben nicht die direkten Entsprechungen von Kleinbuchstaben sind, wie wir dies aus unserem lateinischen Alphabet gewohnt sind.
1
2
toLocaleLowerCase() und toLocaleUpperCase() sind sehr neue Methoden.
3 NS4.x M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
toLocaleLowerCase()
toLocaleUpperCase()
SF/KQ
Tabelle 7.17: toLocaleLowerCase() und toLocaleUpperCase()
4
5 Im Download-Archiv finden Sie im Ordner code\kap07 ein Beispiel mit dem Namen toLocaleLowerCase_toLocaleUpperCase.html.
6
WWW
7.2.4
Methoden zum Arbeiten
7
In diesem Abschnitt finden Sie einige Methoden, um sinnvoll mit StringObjekten arbeiten zu können. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 7.18: Arbeitsmethoden
8
9 Alle Arbeitsmethoden sind in den gängigen Browsern vorhanden. Nur in der Unterstützung der älteren Browser Netscape 2 und 3 sowie Internet Explorer 3 in beiden Versionen gibt es Unterschiede, die Sie bei den Methoden finden.
10
11 concat() Die Methode concat() verbindet zwei Strings. Sie hat also dieselbe Funktion wie der Operator +. In der Syntax gleicht sie der concat()-Methode des Array-Objekts:
12
var String_gesamt = String1.concat(String2);
13
253
Arrays und Strings
Die Methode concat() steht erst ab der vierten Generation von Netscape Navigator und Internet Explorer zur Verfügung. INFO
Ein kleines Beispiel zeigt die Funktionsweise: var text1 = "Nilpferd"; var text2 = " und Giraffe"; var gesamt = text1.concat(text2);
Die Variable gesamt enthält nun den Text »Nilpferd und Giraffe«. slice() slice(Start, Ende) funktioniert für Strings ähnlich wie für Arrays. Als Parameter geben Sie einen Startpunkt an, ab dem die Zeichen des Strings an einen neuen String übergeben werden. Der Endpunkt ist optional und legt fest, bis zu welcher Stelle die Zeichen übernommen werden.
Die Methode slice() steht ebenfalls erst ab der vierten Browsergeneration von Netscape und Internet Explorer zur Verfügung. INFO
Folgendes Beispiel illustriert slice(): Listing 7.26: Ausschnitt aus: slice_string() var text = "Nilpferde"; document.write(text.slice(3, 8));
Das Beispiel liefert die Buchstaben von Position 3 »p« bis vor Position 8 »d« an Position 7). Der Endpunkt (Position 8) wird also nicht in den neuen String übernommen. Abbildung 7.30: Die Zeichenkette von Position 3 bis (exklusive) 8 ist »pferd.«
HALT
Beachten Sie, dass der Index nicht mit 1, sondern mit 0 beginnt. Das erste Zeichen hat also die Position 0. Des Weiteren ist wichtig zu wissen, dass der Originalstring bestehen bleibt und die in den neuen String übernommenen Zeichen nicht aus dem Originalstring herausgelöscht werden. Wenn Sie für den Endpunkt einen negativen Wert angeben, wird er von rechts, also vom Ende des Skripts, gezählt. Ein Endpunkt von –1 ist also das vorletzte Zeichen.
254
Strings
Alternativ können Sie auch die Eigenschaft length verwenden, um das Ende eines Strings herauszufinden. String.length - 2 ist also ebenfalls der vorletzte Buchstabe eines Strings. Beachten Sie, dass Sie als Endpunkt String.length – 1 verwenden, wenn Sie nur bis zum vorletzten Buchstaben ausgeben wollen, denn der Endpunkt wird nicht mit in den neuen String übernommen. 1 split() split(Trennzeichen) teilt einen String in Einzelstrings, die alle als Elemente einem Array übergeben werden. Das Trennzeichen legt fest, an welchen Stellen der String geteilt wird.
2
Die Methode split() gibt es seit Netscape 3 und Internet Explorer 4.
3
Ein Beispiel schafft Klarheit: Das Skript teilt den Namen beim Leerzeichen, da als Trennzeichen " " eingetragen ist. Dies können Sie beispielsweise bei Formulareingabefeldern nutzen, um Eingaben voneinander zu trennen.
INFO
4
Listing 7.27: Einen String mit split() teilen (split_string.html)
5
split() für Strings
8
9
Abbildung 7.31: Der Name besteht nun aus zwei Array-Elementen.
10
11
12
13
255
Arrays und Strings
Ist das Trennzeichen ein leerer String (""), wird jeder Buchstabe geteilt: var text = "Nilpferd"; var text_array = text.split("");
Abbildung 7.32: Jeder Buchstabe ist ein Array-Element.
substring() und substr() substring(Start, Ende) liefert den String von der Startposition bis vor die Endposition. Das Zeichen an der Endposition ist nicht mehr eingeschlossen. Zudem beginnt die Zählung der Zeichen bei 0. substr(Start, Länge) arbeitet statt mit einer Endposition mit der Länge. In der Länge legen Sie also fest, wie viele Zeichen in den neuen String übernommen werden sollen. Die Länge ist allerdings optional. Wird sie weggelassen, nimmt der Interpreter alle Zeichen ab dem Start bis zum Ende des Strings.
INFO
substr() ist nur in der vierten Generation von Netscape und Internet Explorer vorhanden, substring() dagegen bereits seit Netscape 2 und IE 3 in beiden Varianten.
Das folgende kleine Beispiel illustriert die Verwendung beider Methoden: Listing 7.28: substring() und substr() im Einsatz (Ausschnitt aus: substring_substr.html) var text = "Nilpferd"; document.write(text.substring(3, 8)); document.write(""); document.write(text.substr(3, 5));
substring() ist slice() sehr ähnlich. Der einzige Unterschied besteht darin, dass Sie in substring() für die Endposition keine negativen Werte angeben dürfen, sondern in einem solchen Fall mit length arbeiten müssen. Sollten Sie aber gerade diese Funktion nicht benötigen, ist substring() zu bevorzugen, da sie auch in Uraltbrowsern vorhanden ist.
256
Strings
Abbildung 7.33: Beide Methoden erzeugen hier dieselbe Ausgabe.
1
2 Der Netscape Navigator und Mozilla haben Probleme mit substring(), wenn im -Tag als Sprache JavaScript 1.2 angegeben ist. Liegt der Endpunkt dann vor dem Startpunkt, werden beide nicht umgewandelt. Stattdessen wird nur ein leerer String übergeben. Bei den anderen Browsern dagegen liefert eine Umkehrung von Start- und Endpunkt den String in normaler Reihenfolge. Es wird also nichts bewirkt. Für Netscape und Mozilla sollten Sie in diesem Fall JavaScript1.2 nicht als language-Attribut verwenden; dann funktioniert es.
3 HALT
4
5
Auf dem Mac führt ein negativer Wert im Netscape Navigator 4.x für den Parameter Länge von substr() eventuell zu einem Absturz.
6
Ein Beispielskript zum Testen finden Sie im Download-Archiv unter code\ kap07\substring_umgekehrt.html.
7 WWW
charAt(), charCodeAt() und fromCharCode() 8
charAt(Position) liefert das Zeichen des Strings an der als Parameter festgelegten Position. Die Entsprechung charCodeAt(Position) gibt nicht das Zeichen selbst zurück, sondern den ASCII-Code bzw. Unicode des Zeichens. fromCharCode(Code1, Code2) wandelt den ASCII-Code bzw. Unicode in das zugehörige Zeichen um. Als String-Objekt wird hier nicht ein instanziiertes Objekt, sondern String vorangestellt:
9
10
String.fromCharCode(65);
Diese Zeile ergibt also das große A. Des Weiteren können Sie bei fromCharCode() auch optional mehrere Codes angeben. charAt() ist bereits in den alten Browsern ab Netscape 2 und Internet Explorer 3 in der ersten Version vorhanden. Die anderen beiden Methoden sind erst ab der vierten Browsergeneration enthalten.
11
12
INFO
13
257
Arrays und Strings
Das folgende Beispiel zeigt alle drei Methoden im Einsatz: Listing 7.29: Strings in ihrem Zeichencode (zeichencode.html) charAt()
TIPP
Der Unicode von Zeichen wird meist in hexadezimaler Schreibweise angegeben. Diese müssen Sie erst in dezimale Schreibweise umwandeln, bevor Sie den Code in der Methode fromCharCode() einsetzen können.
Abbildung 7.34: Das Zeichen, der Zeichencode und das Zeichen aus dem Zeichencode werden ausgegeben.
7.2.5
Suchen
Eine der häufigsten Anwendungen für Strings ist die Suche. Fragen wie »Wo befindet sich eine bestimmte Textstelle?« oder »Wie können verschiedene Strings abgeglichen werden?« und viele andere mehr lassen sich mit den Suchmethoden beantworten.
REF
258
Viele Suchmethoden erhalten mit regulären Ausdrücken erst all ihre Möglichkeiten. Reguläre Ausdrücke sind genau definierte Textmuster und Suchkriterien. Sie finden eine ausführliche Abhandlung mit Beispielen in Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke«.
Strings
indexOf() und lastIndexOf() Die beiden Methoden indexOf(Suchstring, Start) und lastIndexOf(Suchstring, Start) sind hervorragend zum Suchen geeignet. indexOf() sucht den Suchstring vom Beginn des Strings bis zum Ende. Optional kann eine Startposition, ab der gesucht werden soll, als zweiter Parameter angegeben werden. lastIndexOf() sucht genau wie indexOf(), jedoch nur im String von hinten (rechts) nach vorne (links). Wird eine Startposition angegeben, sucht lastIndexOf() ab dieser Position nach links bis zum Anfang des Strings.
1
2
Kommt der Suchstring im String nicht vor, geben beide Methoden den Wert –1 zurück.
3
indexOf() und lastIndexOf() sind bereits seit den Anfängen (Netscape 2 und Internet Explorer 3 in der ersten Version) in die Browser integriert. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
indexOf()
lastIndexOf()
Tabelle 7.19: indexOf() und lastIndexOf()
4
5
6
Im folgenden Beispiel suchen die Methoden nach dem ersten bzw. letzten »f« im String »Nilpferd und Giraffe«: Listing 7.30: Suchen mit indexOf() und lastIndexOf() (indexOf_lastIndexOf.html)
7
indexOf() und lastIndexOf()
8
9
10
11 Abbildung 7.35: Die Positionen des ersten und letzten »f«
12
13
259
Arrays und Strings
Mehrmals suchen Beim letzten Beispiel hat das Skript das erste und das letzte »f« identifiziert. Das erste »f« aus »Giraffe« blieb dagegen unentdeckt. Wollen Sie alle Vorkommen eines Suchstrings aus einem String herausfinden, müssen Sie sich mit einer Schleife behelfen. Für unser Beispiel sieht sie wie folgt aus: Listing 7.31: Mehrmals suchen (mehrmals_suchen.html) Mehrere Vorkommen suchen
Das Grundprinzip der Schleife ist einfach. In der indexOf-Funktion wird die Startposition der Suche um 1 erhöht, wenn der Suchstring gefunden wurde. Die Suche startet dann also nach dem ersten Fundort neu. Die Abbruchbedingung ist, wenn der Suchstring nicht mehr gefunden wird, indexOf() also –1 zurückliefert. Abbildung 7.36: Alle Fundstellen des »f« werden ausgegeben.
localeCompare() localeCompare() vergleicht zwei Strings miteinander. Die Syntax sieht folgendermaßen aus: var Ergebnis = String1.localeCompare(String2);
Zum Vergleich werden die Unicode-Werte aller Zeichen der Strings nacheinander verglichen. localeCompare() eignet sich gut zum Sortieren für Deutsch, da Groß-/Kleinschreibung und Umlaute berücksichtigt werden. Zusätzlich wird die Sprache des Browsers mit einbezogen. Dies hat im
260
Strings
deutschsprachigen Raum keine Auswirkungen, kann aber beispielsweise bei einem arabischen oder türkischen Browser Änderungen hervorrufen. NS4.x
localeCompare()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 7.20: localeCompare()
1
Sind die Strings in allen Werten gleich, wird 0 zurückgeliefert. Ist String1 kleiner als String2, wird also vor String2 einsortiert, liefert der Browser –1. Ist dagegen String2 vor String1 einsortiert, entspricht der Rückgabewert +1.
2
Listing 7.32: localeCompare() (Ausschnitt aus: localeCompare.html)
3
var text1 = "Nilpferd"; var text2 = "Giraffe"; var erg = text1.localeCompare(text2);
4
In diesem Beispiel sortiert sich »Giraffe« vor »Nilpferd« ein, der Rückgabewert ist also +1 (siehe Abbildung 7.37). 5 Abbildung 7.37: Die 1 besagt, dass String2 vor String1 einsortiert ist.
6
7 8 match(), replace() und search() 9
Die drei Methoden in diesem Abschnitt haben vor allem eines gemeinsam. Sie arbeiten alle mit regulären Ausdrücken als Suchstring. ■
■ ■
match(RegExp) – liefert ein Array mit allen Strings, die auf das Muster des regulären Ausdrucks passen. Passt kein Teil des Strings, wird null zurückgegeben. replace(RegExp, Ersatzstring) – diese Methode ersetzt den mittels des regulären Ausdrucks gefundenen String durch den Ersatzstring. search(RegExp) – arbeitet wie indexOf(), nur mit regulärem Ausdruck. Liefert die Stelle zurück, an der der reguläre Ausdruck zum ersten Mal mit dem String übereingestimmt hat. Ist das Muster des regulären Ausdrucks nicht im String zu finden, liefert die Methode -1.
10
11
12
13
261
Arrays und Strings
Alle drei Methoden sind in den aktuellen Browsern vorhanden (siehe Tabelle 7.20). Ältere Browser wie der Netscape Navigator 2 und 3 und der Internet Explorer 3 in beiden Varianten unterstützen sie nicht. Tabelle 7.21: match(), replace() und search()
NS4.x
match(), replace() und search()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Beispiele zu den regulären Ausdrücken finden Sie in Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke«. REF
7.2.6
toString() und valueOf()
Die beiden Methoden toString() und valueOf() gibt es auch für das StringObjekt. Hier sind sie in ihrer Wirkung allerdings nicht sehr spannend. Sie geben einfach nur den String selbst zurück. Tabelle 7.22: toString() und ValueOf()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
toString()
ValueOf()
Im Download-Archiv finden Sie ein Beispiel unter code\kap07\toString_ valueOf.html. WWW
7.2.7
Neuerungen in JavaScript 1.6 und 1.7
Für Strings gibt es in JavaScript 1.6 und 1.7 einige Neuerungen, die nur in Mozilla-kompatiblen Browsern implementiert sind. Sie können ArrayMethoden auf Strings anwenden. Dabei wird jedes Zeichen eines Strings als Array-Element gesehen. Ähnlich lassen sich auch String-Funktionen beispielsweise bei Arrays verwenden. Diese Funktionalität heißt in der MozillaDokumentation8 auch Generics, hat allerdings nichts mit den in der Programmierung verwendeten Generics zu tun.
8
262
http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6.
Strings
Hier ein Beispiel, das in einer Funktion jeden Buchstaben des Strings einzeln ausgibt: Listing 7.33: Array-Funktionen für Strings (string_generics.html) String-Generics
4
5
6
7 8
9
10
11
12
13
263
Inhalt
8
Document Object Model 1
2
Die bisher in diesem Buch verwendeten Beispiele sind zwar alle darauf ausgelegt, in einem Browser zu laufen, stellen also clientseitiges JavaScript dar. Dennoch können sie sehr einfach in serverseitiges JavaScript umgewandelt werden, denn der Sprachkern und -umfang unterscheidet sich nicht.
3
Dieses Kapitel beschäftigt sich ausschließlich mit der Clientseite: dem WebBrowser. Das DOM (Document Object Model) ist ein Modell aller Objekte im Browser. Dieses Modell zielt darauf ab, Zugriff auf alle Elemente einer Webseite (oder einer XML-Datei) zu gewähren.1 Der Zugriff gibt Ihnen die Möglichkeit, Objekte zu verändern und neue Objekte hinzuzufügen. Ersteres kommt in Webseiten wesentlich häufiger vor.
4
5
Um zu verstehen, was das DOM ist, macht ein einfaches Beispiel den Anfang. Die folgende HTML-Seite besteht aus einer Überschrift und einem Text mit einer Hervorhebung: Listing 8.1:
6
Eine HTML-Seite (dom.html)
7
DOM Überschrift
Text mit Hervorhebung
8 9
10
Im Browser sieht dies sehr unspektakulär aus (siehe Abbildung 8.1). Diese Seite wird aber nun in einem Document Object Model abgebildet. Das führt der Browser automatisch aus, wenn er die Seite lädt. Das DOM der Seite enthält dann alle Elemente der Seite in hierarchischer Ordnung. Das oberste Objekt in der Seite ist immer document. Es steht gewissermaßen über allen anderen Elementen einer Webseite. Den vollständigen DOM-Baum aus Listing 8.1 sehen Sie in Abbildung 8.2.
11
12
13
Das DOM lässt sich also durchaus auch als API (Application Programming Interface) definieren.
265
ndex
1
Document Object Model
Abbildung 8.1: Die einfache HTML-Seite sieht im Browser sehr unspektakulär aus.
INFO
Abbildung 8.2: Der DOM-Baum der HTML-Seite
266
Das DOM umfasst in der allgemeinen Darstellung (siehe Kapitel 6 »Objekte in JavaScript«) die so genannten Browserobjekte. Dies sind Objekte, mit denen Sie auf spezielle Elemente einer Webseite zugreifen können. Sie finden sie in Abschnitt 8.3 »Browserobjekte«. Das hier vorgeführte Beispiel ist ein DOM-Modell für eine einzelne Webseite. Unabhängig davon können in dieser Webseite auch Elemente wie beispielsweise Formulare oder Stilanweisungen vorhanden sein, auf die Sie mit Browserobjekten zugreifen können. Man kann also sagen: Das DOM-Modell einer einzelnen Webseite ist das hierarchische Abbild aller Elemente auf der Webseite, die Browserobjekte dagegen gestatten Zugriff auf bestimmte Objekte und bieten für diese Objekte zusätzliche Eigenschaften und Methoden.
W3C-DOM
Der DOM-Baum2 besteht aus mehreren Ebenen. Die oberste Ebene ist in diesem Beispiel das -Element (übergeordnet ist noch das bereits erwähnte document-Objekt) und die zweite Ebene der - und Bereich. Jedes Element im DOM-Baum wird als Knoten bezeichnet. In Abbildung 8.2 fehlt noch einiges. Die Leerzeichen3, die zur Einrückung im Skript vorhanden sind, stellen nach strenger Auslegung der Regeln zur Bildung eines DOM ebenfalls Textelemente dar. Netscape Navigator und Mozilla sehen dies auch so (siehe Abbildung 8.3), der Internet Explorer dagegen ignoriert Leerzeichen und Tabs.
1
2 Abbildung 8.3: Das DOM der HTML-Seite im Mozilla DOM Inspector wird mit Whitespaces angezeigt.
3
4
5
6
7
8 9
10
8.1
W3C-DOM
11
Der DOM-Baum aus dem vorangegangenen Beispiel (siehe Abbildung 8.2) entspricht schon recht genau dem W3C-DOM. Das W3C hat sich mit dem DOM zum Ziel gesetzt, einen plattformübergreifenden und einen von Programmiersprachen unabhängigen Zugriff auf Inhalt, Struktur und Stil von Dokumenten zu erlauben.4
12
13 2 3 4
Die Bezeichnung Baum stammt von der hierarchischen Aufspaltung in mehrere Zweige. Die Leerzeichen zählen zu den so genannten Whitespaces. Ebenfalls in diese Kategorie fallen Tabs ((Tab)). Siehe http://www.w3.org/DOM.
267
Document Object Model
HALT
Der DOM-Standard ist nicht Teil von ECMAScript, da die ECMA nur den Sprachkern der Skriptsprache definiert, nicht aber den Zugriff auf Elemente in verschiedenen Umgebungen. Das W3C-DOM wird in drei Bereiche unterteilt: Core5, HTML und XML. Der Kernbereich Core enthält ein minimales Interface – eine Sammlung von Schnittstellen. Der HTML-Teil adressiert direkt HTML-Dokumente und XML dient für XML-Dokumente.6 Diese Modularisierung wird weiter vorangetrieben. DOM Level 2 enthält auch ein eigenes Modul für Stile. Das W3C unterscheidet zwischen verschiedenen Leveln7 der DOM-Standards: ■
■
■
INFO
DOM Level 1 – ist der erste DOM-Standard, der es zu einer Empfehlung des W3C gebracht hat. Er basiert ursprünglich auf dem Objektmodell des Netscape Navigators 3 und ihn gab es seit der vierten Browsergeneration. Allerdings erreichten erst der Internet Explorer 6 und Mozilla 1 eine komplette Umsetzung. Das Besondere an DOM Level 1 ist, dass es noch kein Ereignismodell enthält. DOM Level 2 – ist eine Weiterentwicklung von Level 1. Komplett neu sind das Ereignis-Modell und ein eigenes Modell für Stile, also Stylesheets. DOM Level 3 – ist die neueste Entwicklung und seit 2004 eine Recommendation, die die Modularisierung und Bereitstellung von weiteren DOM-Schnittstellen für verschiedenste Dokumenttypen enthält (u. a. XPath). DOM Level 3 ist allerdings zu DOM Level 2 abwärtskompatibel. Im Vergleich zu Level 2 wurden beispielsweise die in einigen Browsern schon implementierten Tastaturereignisse hinzugenommen. DOM 3 Load & Save enthält Ajax-relevante Möglichkeiten, ein Dokument zu serialisieren, sprich in ein per XMLHttpRequest austauschbares Format umzuwandeln.
Die DOM-Implementierung in den Browsern ist nach wie vor recht unterschiedlich. Vor allem Level 3 ist noch kaum in der Praxis angekommen. Einen guten Überblick liefert http://www.webdevout.net/browser_support. php#dom.
8.1.1
Knoten
Ein Dokument in der hierarchischen DOM-Baumdarstellung besteht aus einzelnen Knoten. In der Beispiel-HTML-Seite (siehe Abbildung 8.2) ist also jedes HTML-Tag und jeder Text dazwischen ein eigener Knoten.
5 6 7
268
Kernbereich. Für die XML-Sprachen MathML (für mathematische Dokumente) und SVG (für vektorbasierte Animation) erstellt das W3C jeweils ein eigenes DOM-Modul. Ebenen; hier allerdings verständlicher: Versionen.
W3C-DOM
Die folgende Tabelle zeigt die verschiedenen Knoten-Arten, die für ein HTML-Dokument zur Verfügung stehen. Insgesamt gibt es zwölf KnotenArten, dabei sind jedoch nur sieben für HTML-Dokumente relevant. Der Rest dient zur Arbeit mit XML-Dokumenten. Art (numerisch)
Art
Name
Beschreibung
1
Element
Name des Tags
Ein HTML-Tag
2
Attribut
Name des Attributs
Ein HTML-Attribut
3
Text
#text
Ein Text (auch Whitespaces, wenn nicht ausgeschaltet)
8
Kommentar
#comment
Ein Kommentar (nur HTML-Kommentar )
9
Dokument
#document
Das Dokument selbst
10
DocType
DOCTYPE
Die Document Type Declaration (DTD)
11
Fragment
#document
Ein Teil des Dokuments, der aus einem oder mehreren Knoten bestehen kann
Tabelle 8.1: Knoten-Arten für HTML-Dokumente
1
2
3
4
8.1.2
5
Zugriff
6
Das Knoten-Konzept verrät bereits viel über den Zugriff per DOM. Jeder Knoten ist über das DOM adressierbar. Dafür gibt es zwei unterschiedliche Methoden: ■ ■
7
Direktzugriff auf ein Element – die häufiger in der Praxis eingesetzte Variante Zugriff über die Navigation durch die Knoten-Hierarchie
8
Direktzugriff
9
Der Direktzugriff erfolgt über zwei Methoden des document-Objekts: ■ ■
getElementById("ID") – liefert ein Element mit der ID, die als Parameter übergeben wird. getElementsByTagName("Name") – gibt ein Array mit allen Elementen mit dem als Parameter übergebenen Tag-Namen zurück.
Bei beiden Methoden gibt es häufig Verwirrungen bei der Schreibweise. Die heikelsten Stellen sind: Die ID in getElementById wird mit einem kleinen »d« geschrieben. getElementsByTagName verwendet den Plural (»Elements«).
10
11
12
TIPP
Der Direktzugriff ist daher nicht vollständig unproblematisch, weil er erst in neueren Browsern möglich ist. getElementsByTagName() hat auch im Opera 6 und niedrigeren Versionen nicht funktioniert. Dort stand nur getElementById() zur Verfügung. Da die Abwärtskompatibilität für Netscape 4, Internet
13
269
Document Object Model
Explorer 4 und Opera 6 aber kaum noch Bedeutung hat, ist der Direktzugriff heute die häufigste Form des DOM-Einsatzes. Tabelle 8.2: Die zwei Funktionen für den Direktzugriff
NS4.x M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
getElementById()
getElementsByTagName()
getElementById() getElementById("ID") wird in der Praxis sehr häufig eingesetzt, da es den Zugriff auf genau ein Element erlaubt. Die ID des HTML-Elements, das angesprochen werden soll, muss in Klammern als String – also in Anführungszeichen – stehen. Im HTML-Tag wird die ID als HTML-Attribut zugewiesen.
INFO
Eine ID darf nur einmal im gesamten Dokument vergeben sein. Der Browser liefert bei der Mehrfachvergabe in HTML zwar keinen Fehler, aber der JavaScript-Interpreter weiß dann nicht mehr, welches Element eigentlich zu wählen wäre. Das folgende Beispiel zeigt den Einsatz von getElementById(). Das Skript überträgt die Eingabe von einem Formularfeld in ein anderes, sobald der Nutzer die Schaltfläche anklickt. ■
Zuerst wird einer Variablen das Element mit der ID in Anführungszeichen zugewiesen. var ele = document.getElementById("ein");
■
Anschließend greift document.getElementById() auf das Ausgabefeld zu und ändert den Wert in den Wert des Eingabefelds. document.getElementById("aus").value = ele.value;
■
Die zugehörige Funktion auslesen() ruft der Nutzer auf, wenn er auf die Schaltfläche klickt.
Hier der komplette Quellcode: Listing 8.2:
Der Einsatz von getElementById() (getelementbyid.html)
getElementById()
270
W3C-DOM
Eingabe Ausgabe
Für den Zugriff auf Objekte der Seite sollten Sie das Skript immer erst beim Laden (Attribut onload mit Funktionsaufruf) oder in einem auf der Seite auftretenden Ereignis (z.B. onclick) ausführen, da ansonsten das Skript bereits ausgeführt wird, bevor alle Objekte der Seite geladen sind.
1
2 TIPP
3 Abbildung 8.4: Ein Klick auf die Schaltfläche übergibt den Text im oberen Textfeld in das untere Feld.
4
5
6
7
8 9 getElementsByTagName() Der große Unterschied von getElementsByTagName("Name") zu getElementById() besteht darin, dass nicht bekannt ist, wie viele Elemente mit dem Tag-Namen im Dokument vorhanden sind. Als Rückgabe liefert die Methode also nicht nur einen Variablenwert, sondern ein ganzes Objekt vom Typ NodeList mit allen Knoten, die ein Tag mit dem Namen haben.
10
Der direkte Zugriff auf ein einzelnes Element ist also mit getElementById() wesentlich einfacher zu bewältigen. getElementsByTagName("Name") ist insbesondere dann sinnvoll, wenn Sie eine bestimmte Art von Tags, beispielsweise alle - oder alle
-Tags, gleichzeitig ändern möchten.
12
11
13
Das folgende Beispiel macht sich dies zunutze und ändert die Schriftgröße für alle
-Tags, wenn der Nutzer auf die Schaltfläche klickt: Dazu dient
271
Document Object Model
eine Schleife, die mit length die Länge des Arrays überprüft und die ArrayElemente der Reihe nach ändert: ■
Zuerst wird die Hilfsvariable elemente definiert. Sie erhält das Array mit allen
-Tags: var elemente = document.getElementsByTagName("p");
TIPP
Hilfsvariablen dienen in den meisten Fällen dazu, den Code abzukürzen. In diesem Fall müssen Sie document.getElementsByTagName() nur einmal aufrufen. Ansonsten würde der Code in der Schleife wesentlich länger und unübersichtlicher. ■
Die for-Schleife durchläuft nun alle Elemente des soeben erzeugten Arrays: for (var i = 0; i < elemente.length; i++) {
■
Eine Fallunterscheidung prüft die aktuelle Schriftgröße. Beträgt sie bereits 20 pt (Punkt), wird die Schrift auf 12 pt verkleinert. Ansonsten wird (else) auf 20 pt vergrößert. if(elemente[i].style.fontSize == "20pt") elemente[i].style.fontSize = "12pt"; else elemente[i].style.fontSize = "20pt";
INFO
Das style-Objekt erlaubt einen direkten Zugriff auf die Stileigenschaften des jeweiligen Tags. Es ist Teil des W3C-DOM-Standards und für jedes HTMLObjekt vorhanden. Mehr dazu erfahren Sie in Kapitel 22 »CSS und JavaScript«. Dort finden Sie auch Details darüber, wie Sie die Schriftgröße beispielsweise über den Wechsel der CSS-Klasse ändern. ■
Bei der Schaltfläche wird die Funktion zum Wechsel der Schriftgröße (groesse()) aufgerufen.
Für einen Überblick hier das vollständige Beispiel: Listing 8.3:
getElementsByTagName() im Einsatz (getElementsByTagName.html)
getElementsByTagName()
272
W3C-DOM
Erster Absatz
Zweiter Absatz
Größe ändern
1 Abbildung 8.5: Die Schaltfläche wechselt zwischen kleiner …
2
3
4
5
6 Abbildung 8.6: … und großer Schrift.
7
8 9
10
11
Das -Element hat den großen Vorteil, dass es gegenüber nicht in -Tags stehen muss. Dafür ist es in älteren Browsern, beispielsweise Netscape 4.x, nicht verfügbar.
12 INFO
13
273
Document Object Model
Zugriff über Knoten-Hierarchie Der Zugriff über die Knoten-Hierarchie benötigt vor allem einen Startpunkt. Normalerweise ist dies das document-Objekt als oberstes Objekt der DOMHierarchie. Von diesem Objekt aus hangeln Sie sich dann in die Tiefe. Die hierarchische Struktur entspricht Verwandtschaftsgraden unseres Stammbaumsystems.8 Ein übergeordneter Knoten ist also ein Parent (ein Eltern-Knoten) und ein untergeordneter ein Child (Kind-Knoten). Knoten auf einer Hierarchieebene werden als Siblings (Geschwister-Knoten) bezeichnet. Jedes Knoten-Element besitzt Eigenschaften, die den Zugriff auf benachbarte Elemente erlauben. Die Eigenschaft firstChild greift beispielsweise auf den ersten Kind-Knoten zu, parentNode auf den Eltern-Knoten und nextSibling auf den nächsten Geschwister-Knoten. Abbildung 8.7 veranschaulicht verschiedene Eigenschaften. Die Eigenschaft childNodes enthält alle Kind-Knoten eines Knotens in einem Array. Abbildung 8.7: Die Verwandtschaftsverhältnisse zwischen Knoten
Eine Übersicht über die Eigenschaften und Methoden finden Sie in Abschnitt 8.1.3 »Eigenschaften und Methoden des W3C-DOM«. REF
Alle Elemente auslesen Der Zugriff auf die Knoten des Dokuments kann beispielsweise dazu genutzt werden, die Hierarchie des Dokuments darzustellen. Das folgende Beispiel verwendet dazu eine Schleife und einen rekursiven Funktionsaufruf: ■
Die Funktion zur Ausgabe (ausgabe()) wird beim Laden der Seite aufgerufen.
INFO
Der Zugriff auf den DOM-Baum kann immer erst nach dem Laden erfolgen, da erst zu diesem Zeitpunkt die DOM-Hierarchie vom Browser im Arbeitsspeicher hinterlegt ist. ■
8
274
Sie speichert den Rückgabewert aus der Funktion schleife() in einer Variablen und gibt sie aus. Als erster Parameter wird das documentEs wird auch hawaiianisches System genannt.
W3C-DOM
Objekt übergeben. Dies ist der Start für den Hierarchiezugriff. Der zweite Parameter ist ein leerer String. Er wird später verwendet, um tiefer in der Hierarchie liegende Elemente einzurücken. function ausgabe() { var erg = schleife(document, ""); document.write(erg); } ■
1
Die Funktion schleife() besteht aus einer Schleife. Sie führt Folgendes aus: ■
■
2
Als Anfangswert übernimmt sie den ersten Kind-Knoten des Parameters elemente der Funktion. Anschließend durchläuft sie alle Geschwister-Knoten dieses Parameters (siehe Schleifenänderung ele = ele.nextSibling), bis keiner mehr vorhanden ist (Bedingung: ele != null).
3
for (var ele = elemente.firstChild; ele != null; ele = ele.nextSibling) ■
4
In der Schleife folgen die Anweisungen: Der Sammelvariablen string werden nur die Knoten-Namen (ele.nodeName) der Knoten hinzugefügt, die HTML-Tags sind (ele.nodeType==1).
5
if (ele.nodeType == 1) { string += z + ele.nodeName + ""; } ■
6
Nach der Ausgabe folgt der rekursive Aufruf der Funktion. Als neues Knoten-Objekt wird ele übergeben. Dies ist der Kind-Knoten des Knotens beim letzten Funktionsaufruf – er wurde in der Schleifenanfangsbedingung definiert. Der zweite Parameter erhält als Einrückung zwei HTML-Leerzeichen ( ).
7
8
schleife(ele, z + " "); }
Der rekursive Aufruf sorgt dafür, dass die Schleife in die tieferen Ebenen der Hierarchie gelangt. Die Hierarchie der Aufrufe sieht für das Beispiel wie folgt aus:
9
Schleife // liefert HTML Funktionsaufruf Schleife // liefert HEAD Funktionsaufruf Schleife // liefert TITLE Funktionsaufruf Schleife // Abbruch, da ele == null Schleife // liefert SCRIPT Funktionsaufruf Schleife // Abbruch, da ele == null Schleife // Abbruch, da ele == null Schleife // liefert BODY . . .
10
11
12
13
275
Document Object Model
Vergleichen Sie dies mit dem Ergebnis des Codes oder mit Abbildung 8.8. ■
Nach dem letzten Schleifenabbruch wird die Sammelvariable string zurückgegeben. return string;
Hier sehen Sie den kompletten Code: Listing 8.4: Mit einer rekursiven Funktion lassen sich alle HTML-Tags auslesen (knoten_zugriff.html). 9 Zugriff auf alle Knoten Überschrift
Text mit Hervorhebung
INFO
Soll die DOM-Hierarchie beim Zugriff nicht verändert werden, müssen Sie von außerhalb zugreifen. Am einfachsten funktioniert dies über Frames bzw. einen iFrame.
9
276
Dieses Beispiel funktioniert nur in Browsern, die mit dem W3C-DOM etwas anfangen können. Also nicht im Internet Explorer 4 und im Netscape Navigator 4.x. Der Konqueror hat in älteren Versionen Probleme mit der document.write()-Ausgabe. Geben Sie dort die Informationen beispielsweise in eine Textarea aus.
W3C-DOM
Abbildung 8.8: Ein einfaches JavaScript zeigt die Hierarchie des Dokuments.
1
2
3
4 Zugriffsarten mischen In der Praxis treten oft beide Zugriffsarten gemischt auf. Der erste Zugriff auf ein Element erfolgt dabei meist mit getElementById(), die weitere Arbeit übernehmen Methoden und Eigenschaften, um innerhalb der Knoten-Hierarchie zu arbeiten.
5
6
In Abschnitt 8.1.3 »Eigenschaften und Methoden des W3C-DOM« finden Sie die Eigenschaften und Methoden näher erläutert.
7
REF
Das folgende Beispiel zeigt auf einfache Art die Mischung von direktem Zugriff und Zugriff über die Knoten-Hierarchie. Ziel ist, einen Knoten mit seinen Kind-Knoten zu duplizieren.
8
Grundlage dieses Beispiels ist Listing 8.1. Zusätzlich wurde dem Textabsatz ein id-Attribut mit dem Wert absatz1 zugewiesen. Dieser Absatz soll nun im Skript im Kopf der Seite mit getElementById angesprochen und anschließend geklont werden: ■
9
10
Der Zugriff auf den Absatz erfolgt, wie bereits erwähnt, über getElementById(). var ele = document.getElementById("absatz1");
■
11
Anschließend wird der Absatz mit cloneNode(Tiefe) geklont. Für die Tiefe können Sie true angeben, wenn alle darunter liegenden Knoten (Child Nodes oder Kind-Knoten) auch geklont werden sollen.
12
var klon = ele.cloneNode(true); ■
Bisher sind die geklonten Knoten allerdings nur als Referenz, als eine Art Abbild, in der Variablen klon gespeichert. Um sie wieder einzufügen, benötigen Sie eine weitere Methode, beispielsweise appendChild(Knoten). Vorher muss in der Hierarchie allerdings einen Knoten
13
277
Document Object Model
nach oben gewandert werden, da die geklonten Knoten an das Ende des Dokuments angefügt werden sollen. ele = ele.parentNode; ■
Dann erst kommt appendChild(Knoten) zum Einsatz. Der Parameter Knoten enthält in diesem Beispiel die Referenz auf die geklonten Knoten: ele.appendChild(klon);
Hier im Überblick der vollständige Code: Listing 8.5:
Mischen von direktem Zugriff und Arbeit mit der Hierarchie (dom_zugriff.html)
DOM Überschrift
Text mit Hervorhebung
Abbildung 8.9: Der Absatz der Beispieldatei wurde zusammen mit den darunter liegenden Knoten geklont.
TIPP
278
Die Mischung der Zugriffsarten ist in der Praxis eine der häufigsten Erscheinungsformen. Als Faustregel gilt: Entweder wird getElementById() allein genutzt oder in Verbindung mit dem hierarchischen Zugriff. Dabei müssen Sie allerdings immer beachten, dass der Zugriff über das W3C-DOM nur in neueren Browsern funktioniert. Eine umfangreiche Diskussion dazu finden Sie unter Punkt 8.2 »DOM in verschiedenen Browsern«.
W3C-DOM
8.1.3
Eigenschaften und Methoden des W3C-DOM
In diesem Abschnitt finden Sie die Eigenschaften und Methoden gesammelt, die das W3C-DOM für alle Knoten-Arten definiert. Damit die Wirkungsweise der Eigenschaften aus Tabelle 8.3 nachvollziehbar wird, sehen Sie in der Tabelle jeweils ein Beispiel. Das Seitengerüst, auf das sich dieses Beispiel bezieht, sehen Sie hier:
1
Listing 8.6: Die Arbeitsgrundlage für die Beispiele in Tabelle 8.3 (dom_id.html) childNodes Überschrift
Text mit Hervorhebung
2
3
4
5
6
7
Das Grundgerüst besteht aus einer einfachen HTML-Seite und einer Funktion, die nach dem Laden des Dokuments aufgerufen wird. In der Funktion greift getElementById("absatz") direkt auf den Knoten des
-Tags zu. Eigenschaft
Beispiel
Beschreibung
attributes
Eine Beispielausgabe finden Sie im Download-Archiv unter code\kap08\attributes.html.
Array mit allen Attribut-Knoten eines Dokuments.
childNodes
ele = ele.childNodes; for (var i=0 ; i Überschrift
Text mit Hervorhebung
1
2
3
4
5 Abbildung 8.10: Der Knoten und alle untergeordneten Knoten werden entfernt.
6
7
8 9 Andere, sehr spezielle Eigenschaften und Methoden werden hier nicht beschrieben. So bleiben beispielsweise drei Eigenschaften für XML-Namespaces, namespaceURI, localName und prefix, außen vor. Im DownloadArchiv finden Sie jedoch ein Beispiel unter code\kap08\namespace.html.
10
INFO
11
Neue Elemente hinzufügen Wollen Sie komplett neue Elemente zur DOM-Hierarchie hinzufügen, gilt es einiges zu beachten. Erstellen Sie zuerst ein Element mit createElement("Tag"). Als Parameter wird der Tag-Name in Großschreibung als String übergeben. Bis zu diesem Zeitpunkt ist das Element allerdings noch nicht in der DOM-Hierarchie verankert. Dazu müssen Sie es beispielsweise erst mit appendChild(Knoten) anfügen.
12
13
283
Document Object Model
Ähnlich ist das Vorgehen, wenn Sie einen Text-Knoten einfügen möchten. Hier benötigen Sie zuerst createTextNode("Text")und dann ebenfalls appendChild(). Im folgenden Beispiel wird beides kombiniert eingesetzt, um einen Text aus einem Textfeld beim Anklicken einer Schaltfläche in einen Absatz zu verwandeln: ■
Zuerst erstellt createElement() einen neuen Absatz: var absatz = document.createElement("P");
■
Für den Absatz können Sie in Punktnotation ein Attribut definieren. Für das Beispiel bietet sich die zentrierte Ausrichtung mit align an: absatz.align = "center";
■
Eine Variable nimmt den Wert des Textfelds auf: var formular = document.formular.ein.value;
■
Der Text-Knoten wird mit dem Text aus dem Textfeld erstellt: var text = document.createTextNode(formular);
■
appendChild() fügt den Text-Knoten als Kind an den Absatz an: absatz.appendChild(text);
Beachten Sie, dass der Absatz bisher immer noch nicht sichtbar ist, da er nicht in der DOM-Hierarchie verankert ist. TIPP
■
Abschließend wird der Absatz-Knoten als letzter Kind-Knoten in das -Tag eingefügt: document.body.appendChild(absatz);
Einige Knoten-Arten, beispielsweise das -Tag, haben eigene Objekte. Sie können daher direkt angesprochen werden. INFO
Der vollständige Code sieht folgendermaßen aus: Listing 8.8: Neue Elemente hinzufügen (elemente_hinzu.html) Neue Elemente hinzufügen
284
W3C-DOM
1 Abbildung 8.11: Durch Anklicken wird …
2
3
4
Abbildung 8.12: … ein neuer Absatz hinzugefügt.
5
6
7
8 8.1.4
W3C-DOM und DHTML
9
Wann sollten Sie mit dem W3C-DOM arbeiten? Hier gibt es in JavaScript große Verwirrungen bei den Begrifflichkeiten. Zum einen ähneln die »alten« DOMs der Browser dem W3C-DOM in Teilbereichen (siehe folgenden Abschnitt) und zum anderen ist das W3C-DOM in verschiedenen Browsern unterschiedlich umgesetzt.
10
Bei DHTML – der Mischung von Stylesheets, HTML und JavaScript11 – ist der Zugriff auf Browserobjekte und damit ein DOM das absolut Entscheidende. Schließlich soll sich etwas bewegen und Interaktivität möglich sein. Ebenso entscheidend ist die DOM-Arbeit bei Ajax. Denn die Daten vom Server müssen auch ausgegeben werden und dafür sorgt das DOM. Das W3CDOM ist dafür heute der absolute Standard.
12
11
13
11 Zu dieser Definition erfahren Sie mehr in Kapitel 23 »Dynamisches«.
285
Document Object Model
Wer noch sehr alte Browser wie Netscape 4 und Internet Explorer 4 berücksichtigen möchte, muss allerdings mit einer Browserunterscheidung arbeiten, da diese das W3C-DOM nicht beherrschen. Die kann zu Beginn der kompletten Website getroffen werden, indem verschiedene Browsergruppen unterschiedliche Seiten sehen dürfen. Da dies aufwändig und häufig unpräzise sein kann, können Sie auch zuerst in der DHTML-Anwendung für verschiedene Browser eigenen Code programmieren. Eine Übersicht zu diesem Thema erhalten Sie in Kapitel 14 »Browserunterscheidung«. REF
8.2
DOM in verschiedenen Browsern
Objektmodelle funktionieren alle ähnlich und haben dasselbe Ziel: einfachen Zugriff auf Elemente in der Webseite. Was gut klingt, ist in der Praxis nicht ganz so schön. Tabelle 8.6 zeigt die Verwendung von verschiedenen DOMs in alten und aktuellen Browsern.12 Tabelle 8.6: Die DOMs in den aktuellen Browsern
Object Model
Browser
DOM Level 0
ab NS 2, IE 3 v1; alle aktuellen Browser
DOM Level 0 + Bilder
ab NS 3, IE 4; alle aktuellen Browser IE 3.01 am Mac
NS 4-Erweiterungen (Layer)
NS 4.x
IE 4-Erweiterungen
ab IE 4 teilweise: ab Op 6
IE 5-Erweiterungen
ab IE 5 (Unterschiede Mac und Win) teilweise: ab Op 6
W3C-DOM Level 1
IE 6, NS 6, Moz, Op 7a; K3/S1 teilweise: IE 5, 5.5 (Unterschiede Mac und Win)
W3C-DOM Level 2
FF 2, Op 9 teilweise: IE 6/7, NS 6, 7, Moz, Op 7+, K3/S1
W3C-DOM Level 3 a.
teilweise: IE 6/7, FF2, Op 9, K3/S1
Nicht unterstützt: http://www.opera.com/docs/specs/opera07/#dom.
12 Diese Übersicht erhebt nicht den Anspruch, vollständig oder exakt zu sein. Da sich teilweise einzelne Funktionen in ihrer Wirkung unterscheiden, wäre das vermessen. Vielmehr dient die Tabelle dem schnellen Überblick, ersetzt aber nicht das Testen in allen Browsern.
286
DOM in verschiedenen Browsern
8.2.1
DOM Level 0
Das DOM Level 0 war der Ausgangspunkt auch für das W3C-DOM. Dort geistert häufig noch ein – nie als Standard verabschiedetes – W3C-DOM Level 0 durch die Hallen, das dem DOM Level 0 aus Netscape 2 und 3 relativ genau entspricht. Das DOM Level 0 hat sich insbesondere um die Browserobjekte verdient gemacht. Die wichtigsten heute verwendeten wie window, document und location waren damals Teil des DOM Level 0. Allerdings gab es viele Eigenschaften und Methoden für Netscape 2 und 3 bzw. Internet Explorer 3 noch nicht.
1
Sie sollten heute nicht mehr auf die Kompatibilität mit Netscape 2 und 3 bzw. Internet Explorer 3 achten. DHTML ist beispielsweise mit den alten Browsern nicht möglich, da die einzigen interaktiven Elemente in der Seite Formularfelder, Schaltflächen und Links waren und eine Änderung von CSS-Stilen noch nicht vorgesehen war.
3
2
TIPP
4
5
8.2.2 DOM Level 0 mit Bild-Objekt In Netscape 3 wurde das DOM Level 0 ein wenig erweitert, und zwar um das Bild-Objekt für -Tags. Vielleicht ahnen Sie es? Damit lassen sich Rollover-Effekte realisieren, wenn der Nutzer mit der Maus über das Bild fährt und dieses ausgewechselt wird.
6
7
Diese neue Funktion wurde von Webentwicklern sehr geliebt. Leider gab es sie nicht im Internet Explorer 3, da dieser kurz vor Netscape am Markt war. Microsoft konnte dieses Objekt erst im Internet Explorer 4 bzw. am Macintosh in Release 3.01 einbauen.
8
8.2.3 Netscape 4.x
9
Netscape 4.x brachte dem DOM-Modell einige Neuerungen. Zum einen wurde ein verbessertes Event-Handling-Modell verwendet. Die Mausereignisse wurden zahlreicher und Tastatureingaben konnten erstmals als Ereignisse abgefangen werden. Zum anderen gab es die Möglichkeit, Ereignisse nicht nur bei HTML-Elementen anzugeben, sondern auf Ereignisse auch bei Objekten wie document zu warten. Der Schlüssel dazu war die captureEvents()-Methode, die gewissermaßen ein Ohr auf die Schiene legt, um herauszufinden, ob der Nutzer etwas ausführt.
10
11
12
Neben dem Ereignismodell führte Netscape die an sich gute Idee der Layer in die Browserwelt ein. Das -Tag erlaubte, verschiedene Bildebenen übereinanderzulegen und mit JavaScript flexibel zu positionieren und einund auszublenden.
13
287
Document Object Model
Das große Problem mit den Layern bestand darin, dass sie es nicht zu einem W3C-Standard brachten. Microsoft setzte nicht auf das Layer-Konzept und in allen neueren Browsern werden die Positionierung und alle Aufgaben der Layer von CSS-Befehlen übernommen, die mit JavaScript gesteuert werden können. Layer gibt es nur in Netscape 4.x. In Netscape 6 und 7 sind sie wieder rausgeflogen. Stattdessen setzt auch Netscape nun auf die Positionierung und DHTML mit CSS.
REF
Wollen Sie Netscape 4.x-kompatible DHTML-Anwendungen programmieren, kommen Sie um Layer nicht herum. In modernen Ajax-Anwendungen entscheidet man sich heute allerdings meist gegen Netscape 4.
8.2.4 Internet Explorer 4 Der Internet Explorer 4 machte einen großen Sprung nach vorne. Es war nun möglich, alle Elemente einer Webseite per Skript anzusprechen. Dazu diente allerdings leider kein Standard, sondern das »Sammelbecken«13 document.all. Es enthält alle Elemente einer Webseite.
REF
Soll Ihr DHTML also Internet Explorer 4-kompatibel sein, müssen Sie mit document.all arbeiten. Dies hilft auch dem Opera in den früheren Versionen vor 7. Allerdings wird in modernen Anwendungen vor allem im Ajax-Bereich der Internet Explorer 4 kaum noch berücksichtigt. Das Event-Handling-Modell des Internet Explorers 4 und auch der späteren Versionen unterscheidet sich allerdings von dem des Netscapes und des Firefox bzw. Mozillas (siehe Netscape 4.x). Die Ereignisse sind alle Teil des window-Objekts. Sobald ein Nutzer beispielsweise auf eine Schaltfläche klickt, pflanzt sich das Ereignis bis hoch in das window-Objekt fort. Dies bezeichnet man als Event-Bubbling. Netscape/Mozilla setzt dagegen auf das Hören auf Ereignisse ausgehend vom obersten Objekt.14 Als dritte große Änderung (für Programmierer) im Internet Explorer 4 wurde der CSS-Standard Version 1 zumindest größtenteils umgesetzt. Hier können Sie sich grob merken: Der Internet Explorer 4 setzt einen Großteil des Standards um, der Netscape Navigator 4.x etwas weniger.
8.2.5 Internet Explorer 5 Der Internet Explorer 5 ist insbesondere in einer Hinsicht ein schwieriger Geselle. Auf dem Macintosh ist für ihn ein eigenes Entwicklerteam zuständig. Daher wurden dort beispielsweise die Fähigkeiten des W3C-DOM Level 1 etwas früher integriert. 13 Der programmiertechnisch korrekte Begriff ist Kollektion oder Collection. 14 Dies wird auch als Top-down-Ansatz bezeichnet.
288
DOM in verschiedenen Browsern
Aus Sicht des JavaScript- bzw. JScript-Interessierten brachte der Internet Explorer 5 und noch einmal in stärkerem Maße die Zwischenversion 5.5 viele neue Eigenschaften und Methoden für die bestehenden Objekte. Darunter sind zwar auch viele proprietäre – also dem Internet Explorer vorbehaltene – Eigenschaften, aber auch einige, die ihn näher an die Standards rücken. 1
Darüber hinaus wurden ebenfalls zwei grundlegende Konzepte neu eingeführt: Verhalten oder auch DHTML Behavior. Dies sind kleine Codeschnipsel, die extern gespeichert werden und einem beliebigen HTML-Element auf der Webseite Funktionen hinzufügen.
2
HTML-Anwendungen (HTML Applications oder HTA). Sie dienen dazu, XML-Dateien mit der Dateiendung .hta in den Browser einzubinden.15
3
Diese speziell auf den Internet Explorer beschränkten Themen behandelt dieses Buch aus zwei Gründen nicht:
4
■
■ ■
Es ist nicht genug Platz vorhanden. Andere – standardkonforme oder browserübergreifende – Themen sind wichtiger. Da die Erweiterungen Internet Explorer-spezifisch sind, könnten sie höchstens in einem Intranet zum Einsatz kommen. Allerdings können die Nutzer auch dort über eigene und andere Browser verfügen.
INFO
5
6
8.2.6 W3C-DOM Level 1
7
Das DOM Level 1 ist in der sechsten Generation der großen Browser und auch bei den wichtigsten kleineren Konkurrenten – Konqueror ab 3 und Opera ab 7 – bereits weitgehend integriert.
8
8.2.7
9
W3C-DOM Level 2
Die Level 2-Unterstützung in den Browsern ist nach wie vor unterschiedlich. Wo W3C-DOM Level 1 noch ohne Event-Handling auskam, ist es nun Teil des Standards geworden. Allerdings vertreten die Browser immer noch teilweise eigene Ansätze.
10
11
8.2.8 W3C-DOM Level 3 Seit einigen Jahren gibt es Ansätze, Level 3-Funktionalität auch in die Browser zu integrieren. Eine interessante und ständig aktualisierte Übersicht finden Sie unter http://www.webdevout.net/browser_support.php#dom.
12
13 15 Weiterführende Informationen finden Sie unter http://msdn.microsoft.com/library/default. asp?url=/workshop/author/hta/overview/htaoverview.asp.
289
Document Object Model
8.3
Browserobjekte
Die Browserobjekte sind die integralen Bestandteile des DOM. Der Zugriff über Knoten und die Hierarchie ist zwar oft sinnvoll, in den meisten Fällen benötigen Sie aber vor allem die Eigenschaften und Methoden der Browserobjekte. Eines der wichtigsten Browserobjekte document haben wir bereits ausführlich verwendet. Dieses Objekt ist der DOM-Hierarchie übergeordnet. Eine einfache Ausgabe in JavaScript funktioniert beispielsweise mit diesem Objekt: document.write("Ausgabe");
Die folgenden Abschnitte geben Ihnen einen Überblick über die verschiedenen Browserobjekte. Im dritten Teil dieses Buches, der mit »Webanwendungen« überschrieben ist, werden Sie diese Browserobjekte in allen erdenklichen Formen einsetzen.
INFO
Die hier vorgestellten Browserobjekte gibt es auch in älteren Browsern. Die hier verwendeten Beispiele sind mit allen in diesem Buch getesteten Browsern kompatibel.
8.3.1
window
Das window-Objekt steht für das Browserfenster. Es ist für alle Elemente im Fenster das hierarchisch übergeordnete Objekt, also auch für das Dokument und damit das document-Objekt. Um das document-Objekt anzusprechen, müssten Sie also theoretisch window.document
schreiben. Dies ist allerdings in der Praxis nicht notwendig. Sie müssen window nur dann davor schreiben, wenn Sie direkt eine Eigenschaft oder Methode aus dem window-Objekt verwenden. Die hier aufgeführten Anwendungsmöglichkeiten geben nur einen kurzen Ausblick auf die folgenden Kapitel. Sie können mit dem window-Objekt ■ ■
Text in der Statusleiste ausgeben eine Ausgabe in einem Warnfenster ausgeben; dies haben Sie bereits gesehen: window.alert("Ausgabe");
Abbildung 8.13: Eine Ausgabe mit window.alert
290
Browserobjekte
Statt window.alert können Sie window auch weglassen, also nur alert schreiben. INFO
■
ein neues Browserfenster öffnen oder schließen
Listing 8.9: Ein neues, leeres Fenster öffnen (window_open.html)
1
Neues Fenster öffnen Fenster öffnen
2
3
4
Abbildung 8.14: Ein neues Fenster mit den angegebenen Maßen wurde geöffnet.
5
6
7
8 9
10
11
12 Sie finden das window-Objekt hauptsächlich in Kapitel 16 »Navigationshilfen« und Kapitel 17 »Fenster«. REF
13
window.frame Ein Unterobjekt von window ist window.frame. Es enthält den direkten Zugriff auf einzelne Frames. 291
Document Object Model
Ebenfalls per JavaScript steuerbar sind die so genannten -Elemente. Sie sind zwar in sich abgeschlossene Frames, können aber auf der Seite flexibel positioniert werden; sie fließen also im HTML-Fluss mit. In Kapitel 19 »Frames« steht dieses Objekt im Mittelpunkt. REF
8.3.2 navigator Das navigator-Objekt wird vor allem zur Browsererkennung benötigt. Es liefert interessante Informationen über den Browser des Nutzers. Mehr über die Browsererkennung mit dem navigator-Objekt erfahren Sie in Kapitel 14 »Browserunterscheidung«.
REF
8.3.3 document Das document-Objekt ist allgegenwärtig. Ob für eine einfache Ausgabe mit document.write oder bei der Methode getElementById(), überall ist document als oberste Instanz der Objekt-Hierarchie mit von der Partie. Andere Objekte wie das form-Objekt sind document untergeordnet.
REF
Das document-Objekt verfolgt Sie im gesamten Buch. In der Referenz finden Sie seine Eigenschaften und Methoden vollständig dokumentiert. Ein Beispiel in diesem Kapitel ist Listing 8.8.
8.3.4 form Das form-Objekt repräsentiert alle Formulare und ihre Elemente. Sie können auf die Elemente entweder direkt mit Formularnamen und dann mit dem Elementnamen zugreifen oder Sie verwenden die Kollektion forms[]. Den Direktzugriff finden Sie in verschiedenen Beispielen in den vorangegangenen Kapiteln. INFO
Der Zugriff über die Kollektion forms[] erfolgt über den Namen des Formulars in eckigen Klammern, also forms[Formularname]. Zusätzlich werden alle Formulare in der Kollektion forms[] mit Index gespeichert; das erste Formular hat beispielsweise den Index 0. Der Zugriff sieht dann wie folgt aus: forms[0].Formularelement
Kapitel 20 »Formulare« beschäftigt sich ausführlich mit diesem Thema und dem JavaScript-Zugriff auf die Formulare. REF
292
Browserobjekte
8.3.5 history Das history-Objekt repräsentiert die History des Browsers, also die zuvor besuchten Seiten. Das folgende Beispiel verwendet die Methode back() des history-Objekts, um die ZURÜCK-Schaltfläche des Browsers als normalen Link zu simulieren: 1
Listing 8.10: Die ZURÜCK-Schaltfläche mit dem history-Objekt (history_back.html) Die History Zurück
2
3
4
5 Abbildung 8.15: Ein Klick auf Zurück …
6
7
8 9
10
11
12 In Kapitel 16 »Navigationshilfen« finden Sie weitere Details.
13 INFO
293
Document Object Model
Abbildung 8.16: … und Sie landen dort, wo Sie zuletzt gesurft sind.
8.3.6 location Das location-Objekt enthält die aktuelle URL der Seite. Mit der Eigenschaft href können Sie außerdem auf andere Adressen weiterverweisen. Listing 8.11: Mit der href-Eigenschaft des location-Objekts umleiten (location_href.html) Seite umleiten Auf Markt+Technik umleiten
Abbildung 8.17: Die Umleitung führt …
294
Browserobjekte
Abbildung 8.18: … auf die Markt+TechnikHomepage.
1
2
3
4
5 Wie Sie zeitversetzte Umleitungen und Umleitungen beim Laden der Seite realisieren, erfahren Sie in Kapitel 16 »Navigationshilfen«.
6
REF
7
8 9
10
11
12
13
295
Inhalt
9
Ereignisse und Event-Handler 1
2
Bei einem Ereignis geschieht etwas. Diese etwas laxe Beschreibung trifft trotz allem den Kern der Sache. Ein Ereignis ist, wenn der Nutzer auf eine Schaltfläche klickt, wenn er über ein Element fährt oder wenn die Seite geladen wird. Eine interaktive Anwendung reagiert auf diese Ereignisse.1
3
Um ein Ereignis abzufangen, verwenden Sie so genannte Event-Handler. Bei einem Event-Handler wird, meist in einer Funktion, festgeschrieben, was geschehen soll. In den vorangegangenen Kapiteln haben Sie das schon sehr häufig gesehen.
4
5
Um Ereignisse korrekt abzufangen, benötigt jeder Browser intern ein Ereignis-Modell, das natürlich auch bestimmt, welche Zugriffsmöglichkeiten Ihnen zur Verfügung stehen. Folgende Ereignis-Modelle gibt es: ■ ■ ■ ■
6
Das Netscape Navigator-Modell. Eingeführt in Version 4.x des Browsers. Das Internet Explorer-Modell. Eingeführt wurde es in Version 4, verbessert in Version 5 des Browsers. Das W3C-DOM-Modell, das versucht, die beiden Modelle zu verbinden. Integriert wurde es erstmals in Netsape 6 und Mozilla. Das »alte« Ereignis-Modell, das auch in älteren Browsern funktioniert. Es ermöglicht, dass Event-Handler in HTML-Tags den Aufruf von Funktionen erlauben, beispielsweise ein onload-Attribut im -Tag.
7
8
9
Dieses Kapitel stellt zuerst die drei Modelle in der Theorie vor und zeigt dann, wie sich die Ereignisse anwenden lassen. Hier finden Sie auch das alte Ereignis-Modell.
10
9.1
11
Ereignis-Modelle
Die Ereignis-Modelle wurden in der vierten Version der beiden großen Browser Internet Explorer und Netscape Navigator geschaffen, um die bisher beschränkten Möglichkeiten2 mit einfachen Event-Handlern für Objekte zu erweitern.
12
13
Der Begriff Ereignis und sein englischsprachiges Pendant Event werden hier synonym verwendet. Siehe Abschnitt 9.2.1.
297
ndex
1 2
Ereignisse und Event-Handler
In den Ereignis-Modellen sind vor allem zwei Dinge hinzugekommen, die auch gleichzeitig die Unterschiede zwischen allen drei Ereignis-Modellen darstellen: ■
■
Ereignisse lassen sich nicht mehr nur bei einem Ereignis abfangen, sondern wandern über das DOM.3 Das heißt, ein Ereignis ist nicht nur beim Objekt abfangbar, sondern auch bei anderen Objekten der DOM-Hierarchie. Ereignisse sind Objekte mit Eigenschaften und Methoden.
9.1.1
Netscape Navigator- (und Mozilla-)Modell
Der Netscape Navigator verwendet das Event-Capturing-Prinzip, um Ereignisse abzufangen. Das heißt, das Ereignis beginnt von oben in der DOMHierarchie und wird durchgereicht. Von jedem Objekt kann das Ereignis abgefangen werden. Hat allerdings das window-Objekt bereits das Ereignis abgefangen, wird es nicht zum document-Objekt weitergeleitet. Abbildung 9.1: Event-Capturing
Event-Capturing Für das Event-Capturing muss das Ereignis zuerst mit der Methode captureEvents(Ereignis) abgefangen werden. Diese Methode folgt einer eindeutigen Syntax: Objekt.captureEvents(Event.EREIGNIS);
Für das Ereignis einer Mausbewegung sähe das so aus: document.captureEvents(Event.MOUSEMOVE);
In einem captureEvents()-Aufruf lassen sich auch mehrere Ereignisse mit dem bitweisen Operator für das logische ODER abfangen: document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT);
3
298
Dieser Vorgang heißt auch Event-Propagation.
Ereignis-Modelle
Das Ereignis in captureEvents() muss immer großgeschrieben werden und besteht aus dem Ereignisnamen ohne on, Event immer mit großem Anfangsbuchstaben und dann klein.
HALT
Soll das »Ausschauhalten« nach einem Ereignis wieder aufgehoben werden, verwenden Sie die Methode releaseEvents().
1
document.releaseEvents(Event.MOUSEMOVE);
Es reicht allerdings nicht, das Ereignis abzufangen. Danach muss bei dem Ereignis noch etwas geschehen: Sie rufen eine Funktion für ein Objekt bei dem Ereignis auf:
2
Objekt.Ereignis = Funktion;
3
Die Funktion wird immer ohne Klammern geschrieben, da sie hier ein Event-Handler ist. In einem Beispiel sieht das so aus: 4
document.onmousemove = bewegen;
Die Funktion übernimmt als Parameter dann das Ereignis. Dies wird meist mit dem Buchstaben e geschrieben.
5
function bewegen(e) { Anweisungen; }
6
Neben den zwei wichtigen Methoden zum Abfangen bzw. Freigeben von Events gibt es noch zwei weitere. ■
7
routeEvent(e) leitet ein Ereignis in der DOM-Hierarchie weiter nach unten. Das Ereignis muss als Parameter angegeben sein. routeEvent() wird meist in der Funktion, die das Ereignis übernimmt, verwendet.
8
document.routeEvent(e) ■
handleEvent(e) gibt es nur im Navigator 4.x. Die Methode leitet das Ereignis nicht weiter, sondern auf ein anderes Objekt um. Das Objekt und sein Event-Handler übernehmen die Weiterverarbeitung.
9
Objekt.handleEvent(e)
Das Abfangen von Ereignissen mit captureEvents() ist in den meisten Browsern außer dem Internet Explorer möglich. NS4.x
M/FF
captureEvents()
releaseEvents()
Op
SF/KQ
routeEvent()
handleEvent()
IE4
IE5
IE5.5
IE6
IE7
10
Tabelle 9.1: Event-CapturingMethoden im Navigator-Modell
11
12
13
299
Ereignisse und Event-Handler
Der Konqueror und der Safari besitzen eine Besonderheit: Sie kennen document.captureEvents() nicht, sondern nur window.captureEvents(). event-Objekt Ein Ereignis wird im Event-Modell des Netscape Navigators an die Funktionsreferenz, die beim Ereignis aufgerufen wird, übergeben. Meist verwendet man hierzu den Parameter e. Listing 9.1:
Das onclick-Ereignis wird abgefangen (klick.html).
Geklickt
Das Ereignis lässt sich in der Funktion auch verwenden. Sie greifen darauf einfach über den Parameter zu: Listing 9.2:
Im Netscape Navigator-Modell auf das event-Objekt zugreifen (e_objekt.html)
function klicken(e) { alert(e.type); }
Abbildung 9.2: Netscape 4 gibt hier den Ereignis-Typ zurück.
300
Ereignis-Modelle
Mit dem Schlüsselwort event können Sie auch in einem Event-Handler als HTML-Attribut ein event-Objekt übergeben. onclick="klicken(event)"
TIPP
Das event-Objekt im Netscape Navigator 4.x definiert noch einige weitere Eigenschaften. Verwirrend ist hier, dass viele dieser Eigenschaften in das neue Ereignis-Modell für Netscape 6 und 7 und Mozilla übernommen wurden. Auch Opera 7, der das W3C-DOM-Ereignis-Modell unterstützt, kennt einige dieser Eigenschaften. Eigenschaft
Beschreibung
which
Die gedrückte Maustaste: 1: die linke Maustaste 2: die mittlere, falls vorhanden 3: die rechte Maustaste, falls vorhanden
1
2 Tabelle 9.2: Wichtige Eigenschaften des event-Objekts im Netscape Navigator 4.x
Bei Tastatur-Ereignissen die gedrückte Taste!
layerX layerY
Koordinaten von Layern in Pixeln. Beschränkt auf Netscape 4.x, da nur dieser Layer unterstützt.
pageX pageY
Koordinaten im Dokument in Pixeln, an denen das Ereignis abgefangen wurde. Dies wird meist für Maus-Ereignisse benötigt.
screenX screenY
Koordinaten auf dem Bildschirm in Pixeln, an denen das Ereignis auftrat.
target
Enthält eine Referenz auf das Ziel des Ereignisses, beispielsweise eine Grafik, die der Nutzer mit der Maus überquert hat.
type
Art des Ereignisses.
9.1.2
3
4
5
6
7
8
Internet Explorer-Modell
9
Der Internet Explorer besitzt seit Version 4 ein eigenes Ereignis-Modell, das dem Netscape-Modell diametral entgegensteht und auch dem W3C-Modell nicht gleicht. Hier tritt das Ereignis beim Objekt auf und wird dann in der DOM-Hierarchie nach oben durchgereicht.
10 Abbildung 9.3: Event-Bubbling
11
12
13
301
Ereignisse und Event-Handler
Event-Bubbling Das Event-Bubbling-Konzept des Internet Explorers endet beim windowObjekt. Welche Objekte auf dem Weg nach oben beteiligt sind, können Sie sich einfach anhand der DOM-Architektur vergegenwärtigen. Für einen Link sind das auf jeden Fall link, body und document.4 Für ein Formularelement kommt noch das Formular selbst hinzu. Das Abfangen von Ereignissen ist sehr einfach: Objekt.Ereignis = Funktion;
Also beispielsweise: document.onclick = klicken;
Das kommt Ihnen bekannt vor? Richtig, das entspricht dem Aufruf eines Event-Handlers im Netscape-Modell. Allerdings fehlt beim Internet Explorer das Abfangen des Ereignisses, da es – einfach ausgedrückt – auf dem Weg von unten nach oben sowieso vorbeischaut. event-Objekt Das event-Objekt des Internet Explorers unterscheidet sich gravierend von dem des Netscape Navigators und auch dem des W3C. Das Objekt ist ein Unterobjekt von window, kann also mit window.event
oder nur event
adressiert werden und steht immer bei einem Ereignis-Aufruf zur Verfügung. Sie müssen es nicht als Parameter übergeben. Das folgende Beispiel führt zum selben Ziel wie Listing 9.2. Beachten Sie aber genau die Unterschiede. Wenn Sie diese erkannt haben, sind Ihnen die Grundlagen der beiden Ereignis-Modelle klar geworden. Listing 9.3:
Das event-Objekt im Internet Explorer (e_objekt_ie.html)
Geklickt 4
302
Eine Sonderrolle spielt das -Tag: Wenn Sie darauf mit einer ID zugreifen, können Sie im Internet Explorer sogar unter Windows Ereignisse abfangen. Am Mac ist das nicht möglich und in der Praxis auch nicht sinnvoll.
Ereignis-Modelle
Die Eigenschaft type gibt es sowohl für das event-Objekt von Netscape als auch für window.event im Internet Explorer. Darin erschöpfen sich allerdings die Gemeinsamkeiten. 1 Wie Sie mit den Unterschieden der beiden Ereignis-Modelle und dem eventObjekt zurechtkommen, zeigt Abschnitt 9.2.2 »Browserübergreifend«. REF
2
Die folgende Tabelle gibt eine Übersicht über die Eigenschaften des eventObjekts im Internet Explorer. Einige dieser Eigenschaften wie beispielsweise offsetX und offsetY werden auch von anderen Browsern wie Opera und Konqueror unterstützt. Allerdings erfolgt dies nur mit dem W3C-EventModell und dort dem event-Objekt. window.event gibt es dagegen nur im Internet Explorer. Eigenschaft
IE-Version
Beschreibung
cancelBubble
Ab 4
Beendet das Bubbling für ein Ereignis, wenn es auf true gesetzt wird. Standardeinstellung ist false. Dies ist besonders wichtig, um zu verhindern, dass oben in der Hierarchie auf ein Ereignis in einem Objekt reagiert werden soll.
button
Ab 4
Gibt an, welche Maustaste gedrückt wurde:
3
4 Tabelle 9.3: Wichtige Eigenschaften des window. event-Objekts im Internet Explorer
0: keine
5
6
7
1: die linke 2: die rechte 3: links und rechts
8
4: die mittlere 5: die linke und die mittlere 6: die mittlere und die rechte
9
7: alle drei
toElement
Ab 4
Enthält das Objekt, über dem die Maus bei dem Ereignis war.
wheelDelta
Ab 6
Enthält die Strecke, die der Nutzer das Rad an der Maus gedreht hat. Die Zahl wird in Vielfachen von 120 angegeben und kann positiv oder negativ sein. Sie korrespondiert mit dem Ereignis onmousewheel.
X Y
Ab 4
Die Position des Mauszeigers relativ zu einem übergeordneten Objekt.
clientX clientY
Ab 4
Die Koordinaten innerhalb des sichtbaren Dokumentbereichs in Pixeln. Mehr zu den Koordinaten erfahren Sie in Kapitel 23 »Dynamisches«.
12
offsetX offsetY
Ab 4
Koordinaten innerhalb des jeweiligen Objekts in Pixeln.
13
10
11
303
Ereignisse und Event-Handler
Tabelle 9.3: Wichtige Eigenschaften des window. event-Objekts im Internet Explorer (Forts.)
Eigenschaft
IE-Version
Beschreibung
screenX screenY
Ab 4
Koordinaten auf dem Bildschirm in Pixeln, an denen das Ereignis auftrat.
keyCode
Ab 4
Liefert den Code für die beim Ereignis gedrückte Taste. Mehr hierzu in Abschnitt »Tastatur-Ereignisse«.
altKey
Ab 4
(Alt) ist gedrückt (true) oder nicht (false).
ctrlKey
Ab 4
(Ctrl) ist gedrückt (true) oder nicht (false).
repeat
Ab 5
Gibt true zurück, wenn ein onkeydown-Ereignis öfter ausgeführt wurde. Dies ist beispielsweise bei der Spieleentwicklung mit JavaScript von Bedeutung.
srcElement
Ab 4
Enthält das Element, das das Ereignis ausgelöst hat.
srcFilter
Ab 4
Enthält das Filter-Element, das das Ereignis onfilterchange ausgelöst hat.
type
Ab 4
Art des Ereignisses.
9.1.3
W3C-Ereignis-Modell
Das W3C-Ereignis-Modell5 verbindet Bubbling mit Event-Capturing. Bubbling ist standardmäßig aktiviert. Das heißt, ein einfacher Aufruf des Ereignisses reicht aus: document.onmousemove = bewegen;
Im Gegensatz zum Bubbling im Internet Explorer reicht das W3C-EreignisModell in Netscape 6, 7 und Mozilla bis zum window-Objekt. Die Schreibweise window.onmousemove = bewegen;
ist also auch möglich. Der Opera 7 kennt dagegen nur maximal document, weswegen Sie auf window verzichten sollten. Event-Listener Das zweite interessante Konzept des W3C-Ereignis-Modells ist die der Event-Listener. Mit einem Event-Listener wird ein Ereignis ähnlich dem Event-Capturing abgefangen. Die zugehörige Methode addEventListener() bietet allerdings wesentliche Verbesserungen: document.addEventListener("Ereignis", Funktion, Modell)
addEventListener() lässt sich jedem beliebigen Objekt zuweisen. Die Methode gibt ein Ereignis an6, auf das »gehört« werden soll. Tritt es ein, ruft die Methode die Funktion auf (ohne Klammern, da Funktionsreferenz). Mit 5 6
304
Das Ereignis ist Teil des DOM-2-Standards, der von den Browsern, namentlich dem IE 6, schon besser unterstützt wird als das zugehörige Ereignis-Modell (das gar nicht unterstützt wird). In Kleinbuchstaben ohne on.
Ereignis-Modelle
dem Boolean-Modell geben Sie an, welches Ereignis-Modell zur Anwendung kommt. true steht für Event-Capturing, false für Event-Bubbling. Der besondere Sinn bei Event-Listenern ist, dass einem Ereignis so mehrere Event-Handler zugewiesen werden können. Wenn Sie beispielsweise mehrere Ajax- und JavaScript-Bibliotheken verwenden, überschreiben sich unter Umständen die Event-Handler bei onload. Dies ließe sich mit Event-Listenern vermeiden.
INFO
1
2
Das folgende Beispiel zeigt addEventListener() im Einsatz: Listing 9.4: Event-Listener im Praxiseinsatz (w3c.html) W3C
3
4
5
6
7
Wollen Sie einen Event-Listener wieder aufheben, verwenden Sie removeEventListener("Ereignis", Funktion, Modell) mit denselben Parametern wie vorher bei addEventListener(), da sonst die Zuordnung verloren gehen kann.
8
Die folgende Tabelle gibt nicht nur einen Überblick, welcher Browser die beiden Methoden unterstützt, sondern zeigt auch, welcher bisher das W3CEreignis-Modell unterstützt. NS4.x M/FF
IE4
IE5
IE5.5 IE6
IE7
Op
SF/KQ
addEventListener()
removeEventListener()
9 Tabelle 9.4: addEventListener() und removeEventListener()
10
11
12
event-Objekt Wie Sie im Beispiel aus Listing 9.4 gesehen haben, wird im W3C-Verfahren das Ereignis im Gegensatz zum Internet Explorer-Modell als Parameter übergeben. Das Ereignis selbst unterscheidet sich deutlich von dem bei Net-
13
305
Ereignisse und Event-Handler
scape 4.x. Einige Eigenschaften wurden allerdings aus Kompatibilitätsgründen beibehalten. Sie sind in der folgenden Tabelle mit einem Sternchen gekennzeichnet. Tabelle 9.5: Wichtige Eigenschaften des event-Objekts beim W3C-DOMModell (* schon in Netscape 4.x vorhanden)
Eigenschaft
Beschreibung
cancelBubble
Hält den Bubbling-Vorgang an (true). Standard ist false.
bubbles
Gibt an, ob ein Ereignis-Bubbling aktiv ist (true) oder nicht (false).
button
Die gedrückte Maustaste: 1: die linke Maustaste 2: die mittlere, falls vorhanden 3: die rechte Maustaste, falls vorhandena
layerX layerY*
Koordinaten von Layern in Pixeln. Beschränkt auf Netscape 4.x, da nur dieser Layer unterstützt.
pageX pageY*
Koordinaten im Dokument in Pixeln, an denen das Ereignis abgefangen wurde. Dies wird meist für Maus-Ereignisse benötigt.
screenX screenY*
Koordinaten auf dem Bildschirm in Pixeln, an denen das Ereignis auftrat.
charCode
Zeichencode der auf der Tastatur gedrückten Taste.
keyCode
Code der gedrückten Taste.
altKey
(Alt) ist gedrückt (true) oder nicht (false).
ctrlKey
(Ctrl) ist gedrückt (true) oder nicht (false).
shiftKey
(ª) ist gedrückt (true) oder nicht (false).
target*
Enthält eine Referenz auf das Element, bei dem das Ereignis auftrat.
currentTarget
Enthält eine Referenz auf das Element, bei dem der Event-Listener definiert wurde.
type*
Art des Ereignisses.
timeStamp
Zeitpunkt des Ereignisses.
view
Enthält eine Referenz auf das window-Objekt, in dem das Ereignis aufgetreten ist.
a.
Tabelle 9.6: Browserkompatibilität der Eigenschaften
306
Opera 7 erkennt die Buttons hier mit anderen Nummern. Siehe hierzu Kapitel 30 »Sicherheit« und dort im Speziellen den Abschnitt 30.3.1 »Rechte Maustaste unterbinden«.
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
cancelBubble
bubbles
button
Ereignisse anwenden
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
layerX layerY
pageX pageY
screenX screenY
charCode
keyCode
altKey
ctrlKey
shiftKey
target
currentTarget
type
timeStamp
view
Tabelle 9.6: Browserkompatibilität der Eigenschaften (Forts.)
1
2
3
4
5
6
7 Neben den Eigenschaften gibt es noch zwei Methoden. Methode
Beschreibung
preventDefault()
Stoppt die Standardaktion für einen Browser. Unterbindet bei einem Event mit Event-Listener also das Ausführen der Funktion.
stopPropagation()
Tabelle 9.7: Die Methoden des event-Objekts beim W3C-Modell
8
9
Beendet das Bubbling und Event-Capturing mit Event-Listener.
10 Die Methoden sind in allen W3C-Modell-konformen Browsern vorhanden. INFO
9.2
11
Ereignisse anwenden
Um Ereignisse für alle Browser anzuwenden, müssen nicht unbedingt die Funktionen der Ereignis-Modelle genutzt werden. Die Standardmethoden sowie den Hinweis auf Beispiele in diesem Buch finden Sie im nächsten Abschnitt. Wie Sie die vielen Möglichkeiten der drei Ereignis-Modelle in der Praxis auf einen Nenner bringen, zeigt Abschnitt 9.2.2 »Browserübergreifend«. Den Abschluss bilden einige Besonderheiten beim Internet Explorer.
12
13
307
Ereignisse und Event-Handler
9.2.1
Die Standardmethoden
Natürlich bietet die Arbeit mit den Ereignis-Modellen viel mehr Möglichkeiten. In Teil 2 und 3 des Buches werden Sie einige Beispiele sehen, die ohne Ereignis-Modell nicht möglich wären. Allein die Ajax-Abfrage selbst basiert auf dem Ereignis-Modell und verwendet Event-Handler. Für viele einfache Aufgaben reichen allerdings die in allen Browsern vorhandenen Optionen zur Anwendung von Ereignissen aus: ■
Ereignisse als HTML-Attribut. In der Praxis sicherlich am häufigsten anzutreffen, da hier der Event-Handler direkt als Attribut einem HTMLElement zugewiesen wird.
■
Sie können die Ereignisse auch direkt im Code einem Element zuweisen. Im folgenden Beispiel weist das Skript der Schaltfläche ein onclickEreignis zu:
Listing 9.5:
Ein Ereignis im Skript setzen (ereignis_code.html)
Ereignisse für Elemente
TIPP
Der Funktionsaufruf erfolgt ohne runde Klammern. Als Parameter könnten Sie zusätzlich ein event-Objekt übergeben, dessen Eigenschaften Ihnen dann zur Verfügung stehen. Rückgabe mit return Bei einigen Ereignissen können Sie mit return true oder false zurückgeben und so eine Reaktion erzwingen. Besonders häufig gebraucht wird dies bei onreset und onsubmit in Formularen, wie ein einfaches Beispiel mit onreset aus dem Kapitel 20 »Formulare« zeigt. Hier wird der Nutzer gefragt, ob er ein Formular wirklich löschen möchte:
308
Ereignisse anwenden
Listing 9.6: Rückgabewerte bei Ereignissen (input_reset.html aus Kapitel 20 »Formulare«) Reset überprüfen
1
2
3
4
9.2.2 Browserübergreifend
5
Der Ansatz, mit verschiedenen Ereignis-Modellen browserübergreifend zu arbeiten, ist nicht allzu kompliziert. Ein einfaches Beispiel zeigt dies:7
6
Sie prüfen über das Browserobjekt, das Sie danach verwenden, ob Capturing unterstützt wird. 7
if(document.captureEvents) { document.captureEvents(Event.CLICK); }
8
Anschließend sieht der Funktionsaufruf bei dem Ereignis in allen Browsern gleich aus: document.onclick=ausgeben;
9
Die nächste Unterscheidung müssen Sie in den meisten Beispielen jetzt treffen, wenn es um die Verwendung des event-Objekts geht. Hier bietet sich eine Unterscheidung mit document.all an, da der Internet Explorer ein komplett anderes System einsetzt:
10
Listing 9.7:
Browserunterscheidung beim event-Objekt (browseruebergreifend.html)
11
function ausgeben(e) { if (document.all) { if(event.srcElement.type == "button") alert("Button im IE angeklickt"); } else { if(e.target.type == "button") alert("Button angeklickt"); } } 7
12
13
Komplexere Beispiele finden Sie in den Anwendungskapiteln in Teil 3 des Buches.
309
Ereignisse und Event-Handler
Die obigen Zeilen prüfen die type-Eigenschaft und geben nur für alle Schaltflächen etwas aus. Analysiert man die Fallunterscheidung auch in den exotischeren Browsern, stellt sich heraus, dass der Konqueror das event-Objekt des Internet Explorers und dessen Eigenschaft srcElement verwendet und kennt. Der Grund dafür liegt darin, dass er document.all unterstützt. Allerdings kommt er genauso mit der Netscape- bzw. W3C-Variante klar.
REF
In Teil 3 des Buches finden Sie noch weitere detaillierte Browserunterscheidungen bei Ereignissen. Im Einzelfall gilt allerdings immer, die Modelle parat zu haben (siehe oben) und zu testen.
9.2.3 Besonderheiten des Internet Explorers Der Internet Explorer kocht bei Ereignissen einige besondere Süppchen, die hier nur kurz vorgestellt werden, da sie für die Praxis nicht allzu viel Relevanz besitzen: ■
■
Im -Block kann mit dem for-Attribut ein Element festgelegt werden, mit event ein Event-Handler. Da diese Variante den Code im -Block bei Browsern, die nicht unterstützen, sofort ausführt, ist sie in der Praxis ungeeignet. Die Methode attachEvent("Ereignis", Funktion) fügt einem Objekt ein Ereignis hinzu, detachEvent("Ereignis", Funktion) entfernt es wieder. Dies entspricht der Event-Listener-Funktionalität. Als Rückgabewert erhält attachEvent() einen Boolean, ob die Anbindung geklappt hat. Allerdings sind die Methoden auf den Internet Explorer ab Version 5 beschränkt und entsprechen leider nicht dem W3C-Standard. Objekt.attachEvent("onclick", ausgabe);
■
setCapture() dient dazu, alle Maus-Ereignisse für ein Objekt (onmousedown, onmouseup, onmousemove, onmouseover, onmouseout, onclick und ondblclick) abzufangen. Sie sehen einen Event-Capturing-Ansatz im Internet Explorer, allerdings nur ab Version 5. Das Gegenstück ist releaseCapture(), es hebt das Capturing wieder auf. Da immer nur eine Capturing-Aktion zur gleichen Zeit stattfinden kann, muss releaseCapture() nicht mehr an das Objekt gebunden sein, sondern lässt sich auch als document.releaseCapture() verwenden.
9.3
Ereignisse
In älteren Browsern waren Ereignisse immer an bestimmte Elemente der Seite gebunden. Dies hat sich mit Internet Explorer 4 und Mozilla 1/Firefox geändert. Mittlerweile gibt es Ereignisse für fast alle Elemente. Vor allem der Internet Explorer besitzt eine geradezu unüberschaubare Liste von Ereignissen. Um ein wenig Ordnung in das Chaos zu bringen, fasst die folgende Tabelle die wichtigsten Ereignisse zusammen. Die Bindung an
310
Ereignisse
bestimmte Objekte wird jeweils für den Netscape Navigator 4 und alle späteren Browser (»And.« für andere Browser) angegeben. Fehlt eine Angabe des Netscape Navigators 4.x, gibt es das Ereignis für diesen Browser noch nicht. Ereignis
Bindung NN 4/And. (IE 4+, FF 1+ etc.)
Beschreibung
NN 4: Link, Buttons
beim Mausklick
Maus
onclick
Tabelle 9.8: Wichtige Ereignisse in JavaScript
1
2
And.: alles
ondblclick
NN 4: Link
beim Doppelklick
And.: alles
onmousedown NN 4: Link, Buttons
3 beim Drücken der Maustaste
And.: alles
onmouseup
NN 4: Link, Buttons, Layer
4
beim Loslassen der Maustaste
And.: alles
onmouseover NN 4: Link, Bilder, Layer
5
beim Bewegen des Mauszeigers über ein Element
And.: alles
onmouseout
NN 4: Link, Bilder, Layer
beim Verlassen eines Elements mit dem Mauszeiger
6
And.: alles
onmousemove NN 4: Event-Capturing
beim Bewegen der Maus
And.: alles
7
Tastatur
onkeypress
NN 4: Textfelder
beim Drücken einer Taste
8
And.: Textfelder
onkeydown
NN 4: Textfelder
beim Herunterdrücken einer Taste
And.: Textfelder
onkeyup
NN 4: Textfelder
9 beim Loslassen einer Taste
And.: Textfelder
10
Ändern
onchange
NN 4: Auswahlliste, Textfelder
beim Ändern eines Elements
And.: Formularelemente
11
Formulare
onsubmit
NN 4: Formulare
beim Übertragen
12
And.: Formulare
onreset
NN 4: Formulare
beim Zurücksetzen
And.: Formulare
13
311
Ereignisse und Event-Handler
Tabelle 9.8: Wichtige Ereignisse in JavaScript (Forts.)
Ereignis
Bindung NN 4/And. (IE 4+, FF 1+ etc.)
Beschreibung
Fokus und Auswählen
onfocus
NN 4: Formularelemente
beim Erhalt des Fokus
And.: Formularelemente
onblur
NN 4: Formularelemente
beim Verlieren des Fokus
And.: Formularelemente
onselect
NN 4: Textfelder, File-Upload
beim Auswählen
And.: Textfelder, File-Upload Bewegen und Skalieren (meist Fenster)
onmove
NN 4: Fenster
beim Bewegen (nur Netscape Navigator 4.x)
onresize
NN 4: Fenster
beim Ändern der Größe
And.: Fenster
onscroll
And.: Fenster
beim Scrollen
NN 4: Layer, Fenster, Bilder
beim Laden
Laden
onload
And.: Fenster, Frameset, Objekte
onunload
NN 4: Fenster
beim Entladen (meist beim Verlassen der Webseite)
And.: Fenster, Frameset Fehlerhandling
onabort
NN 4: Bilder
beim Abbrechen
And.: Objekt
onerror
NN 4: Bilder
bei einem Fehler
And.: Fenster, Frameset, Objekt
INFO
Für die vielen zusätzlichen Ereignisse des Internet Explorers finden Sie unter http://msdn.microsoft.com/workshop/author/dhtml/reference/events.asp eine ausführliche Referenz.
9.4
Tastatur-Ereignisse
Tastatur-Ereignisse abzufangen gehört zu den eher seltenen Aufgaben eines JavaScript-Programmierers. Wenn es aber doch vorkommt, lässt es sich relativ einfach bewerkstelligen. Kernstück sind zwei Elemente: ■ ■
312
Eine Browserunterscheidung zwischen Internet Explorer, Netscape 4.x und dem Rest. Die Umwandlung des Tastencodes in den entsprechenden Buchstaben. Hierzu gibt es beim Internet Explorer eine Methode fromCharCode() des String-Objekts.
Tastatur-Ereignisse
Hier der vollständige Code: Listing 9.8: Tastatur-Ereignisse (tastatur.html) Ereignisse für Elemente Taste drücken
1
2
3
4
5
6
7 Abbildung 9.4: Die Ausgabe liefert ein »g«.
8
9 10
11
12 Im Konqueror 3.1 funktioniert nur onkeydown, nicht aber onkeypress und onkeyup. In älteren Versionen funktioniert auch dies nicht. Den Bug-Report finden Sie aktuell unter http://bugs.kde.org/show_bug.cgi?id=42918.
13 TIPP
313
Teil 2 Ajax In diesem Buchteil geht es um Hintergründe und Grundlagen rund um das XMLHttpRequest-Objekt, die Basis aller Ajax-Anwendungen. Im Detail werden die verschiedenen Methoden zum Datenaustausch von Text über XML bis hin zu JSON untersucht. Außerdem löst der Teil einige wichtige Ajax-Probleme.
317
Architektur und Grundlagen
10
343
XML
11
363
JSON
12
371
Problemlösungen
13
Inhalt
10 Architektur und Grundlagen 1
Ajax steht für Asynchronous JavaScript + XML. Der Name ist – geben wir es ehrlich zu – ein wenig künstlich. Jesse James Garett1 schrieb im Jahr 2005 einen Artikel, in dem er den Begriff prägte. Die eigentliche Intention war damals Marketing in eigener Sache. Das hat ohne Übertreibung hervorragend geklappt. Und auch wenn Sie nach Lektüre dieses Kapitels wissen, dass technologisch hinter Ajax nur ein einfaches JavaScript-Objekt steckt, hat der Begriff doch eine neue Generation von Webanwendungen geprägt und ist als Teil des Web 2.0 (und 3.0 etc.) nicht mehr wegzudenken.2
2
Dieses Kapitel beginnt mit einem ersten Beispiel, das Sie Schritt für Schritt an einen Ajax-Aufruf heranführt. Die Details zu den einzelnen Schritten finden Sie dann in den weiteren Abschnitten des Kapitels.
5
3
4
6
10.1
Ein erstes Beispiel 7
Ein Ajax-Beispiel besteht meist aus mindestens zwei Dateien: Eine liefert die Inhalte vom Server, die andere verwendet das XMLHttpRequest-Objekt, um die Inhalte vom Server abzufragen. Dementsprechend beginnt der erste Schritt darin, die serverseitige Datei anzulegen. 1.
8
Legen Sie eine einfache Textdatei an, die den Namen daten.txt trägt.
Sie können alternativ zu einer einfachen Textdatei auch eine Datei in einer beliebigen serverseitigen Technologie verwenden, die einen String, sprich eine einfache Zeichenkette, zurückliefert.
9 INFO
10
Testwebserver aufsetzen Für Ajax-Beispiele benötigen Sie einen Testwebserver. Der Internet Explorer führt keine XMLHttpRequest-Aufrufe aus lokalen Dateien aus. Der Firefox und andere Browser tun dies zwar, aber dennoch ist ein lokaler Testwebserver sinnvoll. In Frage kommen beispielsweise der Internet Information Service (IIS) unter Windows oder der Apache, der für die meisten Betriebssysteme zur Verfügung steht. Empfehlenswert ist für den lokalen Testrechner ein vorgefertigtes Installationspaket wie XAMPP (http://www.apachefriends.org/).
2
12
13
Zu finden ist der Artikel unter http://www.adaptivepath.com/publications/essays/archives/ 000385.php. In der Tat ist Ajax im Vergleich zum Begriff Web 2.0 sogar noch recht gut definiert und in den meisten Fällen mehr Technik als Marketing.
317
ndex
1
11
EXKURS
Architektur und Grundlagen
Bei lokalen Dateien greift die Sicherheitseinstellung von Ajax, so dass nur Daten von derselben Domain abgerufen werden können. Das heißt, Sie können nicht vom lokalen Webserver aus auf Dateien eines anderen Webservers, beispielsweise bei einem Hoster, zugreifen. 2.
Schreiben Sie in die Textdatei einen beliebigen Text, der dann aus der Ajax-Anwendung abgefragt werden soll, z.B.: Ausgabe per Ajax
3. Legen Sie als Nächstes auf dem Webserver eine Datei namens ajax_basic.html an, die die Daten aus der txt-Datei abfragt. 4. Erstellen Sie in der Datei ein -Element für die Ausgabe: Hier landet die Ajax-Ausgabe
5. Als Nächstes müssen Sie einen -Bereich anlegen und dort das XMLHttpRequest-Objekt erstellen. In der einfachsten Variante erstellen Sie das Objekt mit: var xml_http = new XMLHttpRequest();
Allerdings funktioniert das nur in Browsern, die das Objekt nativ unterstützen. Eine Ausnahme sind hier Internet Explorer-Versionen vor Version 7. Um diese Browser auch zu berücksichtigen, benötigen Sie ein wenig mehr Code. Im Abschnitt 10.3 »Historie« finden Sie eine Erläuterung, hier zuerst einmal nur der Code: var ms_xml = new Array( "Microsoft.XMLHTTP", "MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP.2.0" ); var xml_http = null; if (window.XMLHttpRequest) { try { xml_http = new XMLHttpRequest(); } catch (e) { } } else { for (var typ in ms_xml) { try { xml_http = new ActiveXObject(ms_xml[typ]); break; } catch (e) { //weiter } } }
318
Ein erstes Beispiel
6. Anschließend prüft eine Fallunterscheidung, ob das XMLHttpRequestObjekt richtig angelegt wurde. 7. Wenn ja, wird mit der Methode open() eine neue Verbindung initialisiert: xml_http.open('GET', 'daten.txt', true);
Der erste Parameter ist die Übermittlungsmethode, das so genannte HTTP-Verb. GET heißt, dass Daten, die übergeben werden, an die URL gehängt werden. Mehr dazu im Abschnitt 10.7 »Werte an den Server übermitteln«. Hier gibt es allerdings noch keine Daten, die an die serverseitige Datei übertragen werden. Als Nächstes folgt die URL zur serverseitigen Datei, hier unserer Textdatei. Der dritte Parameter gibt an, ob die Übertragung asynchron oder synchron erfolgt. Mehr dazu im Abschnitt 10.2 »Architektur« und 10.4.1 »XMLHttpRequest als globale Variable«. 8. Die Rückgabe des Ajax-Aufrufs muss nun noch abgefangen werden. Dazu kommt das Ereignis onreadystatechange zum Einsatz. Es erhält eine Funktion als Event-Handler zugewiesen. In der Funktion prüfen Sie, welchen Status die Ajax-Abfrage hat. Beim Status 4 – Abfrage beendet –, übernehmen Sie das Ergebnis mit der Eigenschaft responseText und geben es im vorhin vorbereiteten -Block aus:
1
2
3
4
5
xml_http.onreadystatechange = function() { if (xml_http.readyState == 4) { var ausgabe = xml_http.responseText; document.getElementById("ausgabe").innerHTML = ausgabe; } };
6
7
Sie können hier, statt die Event-Handler-Funktion direkt zu definieren, auch eine andere Funktion mit dem Funktionsnamen referenzieren:
8
xml_http.onreadystatechange = ergebnis;
Die verschiedenen Status und das Abfangen des übermittelten Ergebnisses betrachten wir im Abschnitt 10.6.2 »Antworttypen und Austauschformate« noch im Detail. 9. Zum Schluss schicken Sie Ihre Ajax-Abfrage noch ab. Dies erledigt die Methode send():
9
10
xml_http.send(null);
11
Im Folgenden sehen Sie noch einmal den kompletten Quellcode der HTMLDatei, die den ersten Ajax-Aufruf startet:
12
13
319
Architektur und Grundlagen
Listing 10.1:
Ein grundlegendes Ajax-Beispiel (ajax_basic.html)
Ajax-Ausgabe Hier landet die Ajax-Ausgabe
320
Architektur
Abbildung 10.1: Der String aus der Textdatei wird per Ajax-Aufruf angezeigt.
1
2
3
10.2
Architektur 4
Die Ajax-Architektur wird schon mit dem ersten Beispiel aus dem letzten Abschnitt deutlich. Basis ist das XMLHttpRequest-Objekt. So läuft das Ganze ab:
5
1.
Der Nutzer klickt auf einen Link oder tippt die URL zur Ajax-Anwendung ein. 2. Der Webserver liefert per HTTP3 eine normale HTML-Seite. In der HTML-Seite befindet sich der Code für den Ajax-Aufruf.4 In der Praxis liegt er meist in einer externen JavaScript-Datei, die in der HTML-Seite eingebunden ist. 3. Der Ajax-Aufruf erfolgt dann im Hintergrund, ohne dass sich die Seite neu lädt. Er ist eine normale HTTP-Anfrage an den Webserver. Wann der Aufruf erfolgt, hängt von dem JavaScript-Ereignis ab, das den AjaxAufruf startet. Im Beispiel aus dem letzten Abschnitt erfolgt der Aufruf gleich beim Laden der Seite. Mehr zum »Timing« finden Sie in Kapitel 13 im gleichnamigen Abschnitt 13.4. 4. Die per Ajax aufgerufene serverseitige Datei wird vom Interpreter der serverseitigen Technologie verarbeitet. Hier können beispielsweise noch Daten aus der Datenbank geholt werden. Theoretisch können die Datei mit dem Ajax-Aufruf und die serverseitige Datei auch ein und dieselbe Datei sein. Die serverseitige Technologie entscheidet dann, was sie jeweils ausliefert: die ganze Datei oder nur die Antwort für den Ajax-Aufruf. Dieses Modell ist jedoch bei Aufrufen von Hand etwas unübersichtlich und nicht unbedingt zu empfehlen. Einige AjaxFrameworks (siehe z.B. Xajax oder Microsoft ASP.NET AJAX in den Kapiteln 25 und 27) verwenden allerdings eine solche Architektur.
6
7
8
9
10 11
INFO
12
13 3 4
Die Ajax-Kommunikation ist eine normale Webkommunikation über das HyperText Transfer Protocol (HTTP, http://tools.ietf.org/html/rfc2616). Der Begriff »Aufruf« ist hier gleichbedeutend mit »Anfrage« oder auch der englischen Variante »Request«.
321
Architektur und Grundlagen
5. Die serverseitige Datei liefert einen String oder XML zurück. 6. Die Rückgabe des Ajax-Aufrufs wird per Event-Handler abgefangen. 7. Anschließend werden die Daten in der JavaScript-Anwendung weiterverarbeitet. Abbildung 10.2: Die AjaxArchitektur
Browser HTML mit JS
JavaScriptInterpreter + XMLHttpRequest
INFO
Abbildung 10.3: LiveHTTPHeaders für den Firefox zeigt den AjaxDatenverkehr.
322
Server
Nutzer Anfrage an URL Antwort
Anfrage Antwort als String oder mit XML
Webserver . HTML mit JS
Serverseitige Technologie .php, .aspx etc.
In der Ajax-Architektur können beliebig viele Ajax-Aufrufe abgesetzt werden. Mehr zum eventuell problematischen »Timing« lesen Sie im gleichnamigen Abschnitt in Kapitel 13.
Historie
HTTP-Verkehr »belauschen« Um Ajax-Aufrufe zu debuggen, bietet es sich an, den HTTP-Datenverkehr zu beobachten. So sehen Sie, ob ein Ajax-Aufruf klappt oder ob es Probleme gibt. Dazu gibt es für diverse Browser passende Erweiterungen. Einen Überblick erhalten Sie in Kapitel 29 »Debugging«.
EXKURS
1
10.3 Historie 2
Die Geschichte hinter Ajax ist schnell erzählt. Das XMLHttpRequest-Objekt ist eine Erfindung von Microsoft. Gedacht war es für den Outlook Web Access, die Webschnittstelle von Microsofts E-Mail-Programm Outlook. Mit dieser Schnittstelle sollten die Microsoft-Entwickler die Möglichkeit haben, eine Weboberfläche zu bauen, die deutlich mehr kann als klassische Webanwendungen. Allerdings stand 1998, als die Entwicklung begann, das XMLHttpRequest-Objekt noch nicht als Einziges im Vordergrund. Es gab beispielsweise das Remote Scripting5 mit teilweise auf iFrame basierenden Lösungen.
3
4
5
Der Begriff Ajax schließlich geht auf den schon erwähnten Jesse James Garett zurück, der das damals schon von Google und einigen anderen Pionieren wie dem mittlerweile von Yahoo! übernommenen Oddpost verwendete Objekt in eine moderne Abkürzung goss und ein wenig Marketinginformationen dazugab.
6
7
10.4 XMLHttpRequest browserübergreifend 8
Rein historisch gesehen gibt es bei dem XMLHttpRequest-Objekt unterschiedliche Implementierungen. Aber keine Sorge, bevor Sie an schlimme Kleinigkeiten denken, die ganze Anwendungen zerstören, sei zur Beruhigung gesagt, dass die Unterschiede nur das Erstellen des Objekts betreffen. Alles andere – sprich die Methoden wie open() und send() bzw. die Eigenschaften wie readyState und das Ereignis onreadystatechange – sind in allen Implementierungen gleich.
9
10
Die Unterschiede beim Erstellen des XMLHttpRequest-Objekts bestehen darin, dass das Objekt im Internet Explorer ursprünglich als ActiveX-Objekt, sprich als Erweiterung, eingeführt wurde. Dies geschah in Version 5, das heißt, auf Internet Explorer 4 müssen Sie bei Ajax-Anwendungen verzichten (genau wie auf Netscape 4.x). Der Internet Explorer implementiert XMLHttpRequest erst in Version 7 als natives Browserobjekt. Erst in dieser Version ist also der Zugriff auf das Objekt genauso wie in Mozilla, Firefox, Opera und Safari bzw. Konqueror.
5
11
12
13
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rmscpt/Html/rmscpt.asp.
323
Architektur und Grundlagen
Tabelle 10.1: Die XMLHttpRequestUnterstützung6
6
NS4.x M/FF
XMLHttpRequest nativ XMLHttpRequest ActiveX
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
ab 1.0
ab 8.0 ab 1.2/3.3
Hinzu kommt, dass der Internet Explorer verschiedene Versionen von MSXML, der XML-Bibliothek, die im Browser implementiert ist, kennt. Das heißt, für eine gute Browserunterscheidung müssen die verschiedenen Varianten berücksichtigt werden.7
TIPP
In der Praxis kommt meist ein Ajax-Framework zum Einsatz (siehe Kapitel 25 bis 28). Das Framework kapselt dann das XMLHttpRequest-Objekt und bietet eine einfache Möglichkeit, Ajax-Aufrufe abzusetzen, ohne dass Sie sich um die Browserunterscheidung kümmern müssen. Das hat auch den Vorteil, dass Sie bei Updates immer nur das Framework neu einspielen müssen. Für dieses und die nächsten zwei Kapitel beschäftigen wir uns allerdings mit dem Kern von Ajax und schreiben die Browserunterscheidung deswegen selbst. Dies liegt auch daran, dass alle Ajax-Frameworks leicht unterschiedlich arbeiten und wir hier noch keine Entscheidung für ein bestimmtes Framework treffen wollen. Eine Browserunterscheidung für Ajax kann auf viele verschiedene Arten realisiert werden, wie ein Blick in die Lösungen der verschiedenen Artikel, Bücher und Frameworks zeigt. Eine extrem kurze Variante sieht z.B. so aus: var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : ((window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : false);
Die grundlegenden Bestandteile sind aber immer gleich. Am wichtigsten ist die Prüfung, ob das XMLHttpRequest-Objekt vorhanden ist. Diese Prüfung auf ein Browserobjekt wird in Kapitel 14 noch einmal ausführlich erläutert, da sie in JavaScript-Anwendungen häufig vorkommt. ■
Als Erstes prüfen Sie, ob das XMLHttpRequest-Objekt als natives Browserobjekt im Browser implementiert wurde. if (window.XMLHttpRequest) { try { xml_http = new XMLHttpRequest(); } catch (e) { } }
6 7
324
Das W3C (World Wide Web Consortium), der Gralshüter der Webstandards, arbeitet aktuell an einem Standard für das XMLHttpRequest-Objekt: http://www.w3.org/TR/XMLHttpRequest/. Eine Übersicht über die XML-Varianten im Internet Explorer: http://msdn.microsoft.com/ library/en-us/xmlsdk/html/5016cf75-4358-4c1f-912e-c071aa0a0991.aspx.
XMLHttpRequest browserübergreifend
■
Wenn das Objekt vorhanden ist, wird versucht, es anzulegen. Damit keine Fehlermeldung ausgegeben wird, verwenden wir einen trycatch-Block. Fehler, die im Code im try-Bereich auftreten, werden hier an den catch-Block weitergegeben und dort behandelt. Da hier keine weitere Behandlung erfolgt, werden eventuelle Fehler ignoriert. Details zu try-catch finden Sie in Kapitel 29 »Debugging«. Für den Internet Explorer müssen Sie ein wenig mehr Aufwand betreiben, da verschiedene Signaturen für das ActiveX-Objekt denkbar sind. Der einfachste Weg ist, die Signaturen in einem Array anzulegen:
1
2
var ms_xml = new Array( "Microsoft.XMLHTTP", "MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP.2.0" ); ■
3
4
Das Array mit den Signaturen wird dann per Schleife durchlaufen. In einer try-catch-Anweisung versuchen Sie, das ActiveXObject zu erstellen. Sobald eine der Signaturen im Browser funktioniert, wird die Schleife mit break verlassen:
5
6
for (var typ in ms_xml) { try { xml_http = new ActiveXObject(ms_xml[typ]); break; } catch (e) { //weiter } }
7
8
Hier noch einmal der komplette Code für die Browserunterscheidung in der Übersicht: Listing 10.2:
9
Eine grundlegende Browserprüfung zum Erstellen des XMLHttpRequest-Objekts
var ms_xml = new Array( "Microsoft.XMLHTTP", "MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP.2.0" ); var xml_http = null; if (window.XMLHttpRequest) { try { xml_http = new XMLHttpRequest(); } catch (e) { } } else {
10 11
12
13
325
Architektur und Grundlagen
for (var typ in ms_xml) { try { xml_http = new ActiveXObject(ms_xml[typ]); break; } catch (e) { //weiter } } }
Diesen Aufruf sollten Sie nun noch in einer Funktion oder einer Methode kapseln, damit Sie ihn jederzeit wiederverwenden können. Auch dafür gibt es diverse Varianten. Die wichtigste Frage für das Kapseln ist, wie Sie das XMLHttpRequest-Objekt an den Event-Handler übergeben. Hier gibt es zwei Möglichkeiten: ■ ■
als globale Variable gekapselt in ein Objekt
Beide Varianten haben ihre Vor- und Nachteile: Die globale Variable ist etwas einfacher handhabbar und eignet sich gut für kleinere Anwendungen. Der in einem Objekt gekapselte Aufruf erlaubt völlig unabhängige modulare Einheiten.
10.4.1 XMLHttpRequest als globale Variable Wenn Sie XMLHttpRequest als globale Variable einsetzen möchten, kapseln Sie das Erstellen des Objekts in einer Funktion (hier create_xml_http()) und definieren die Variable außerhalb. Immer wenn Sie dann einen Aufruf benötigen, verwenden Sie die Funktion: Listing 10.3:
Ajax-Objekt per globale Variable (Ausschnitt aus: ajax_globale_variable.html)
var xml_http = null; function create_xml_http() { if (xml_http == null) { var ms_xml = new Array( "Microsoft.XMLHTTP", "MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP.2.0" ); if (window.XMLHttpRequest) { try { xml_http = new XMLHttpRequest(); } catch (e) { } } else { for (var typ in ms_xml) { try {
326
XMLHttpRequest browserübergreifend
xml_http = new ActiveXObject(ms_xml[typ]); break; } catch (e) { //weiter } } }
1
} } window.onload = function() { create_xml_http(); if (xml_http) { xml_http.open('GET', 'daten.txt', true); xml_http.onreadystatechange = function() { if (xml_http.readyState == 4) { var ausgabe = xml_http.responseText; document.getElementById("ausgabe").innerHTML = ausgabe; } }; xml_http.send(null); } };
2
3
4
5
Im vorliegenden Beispiel wird die Funktion beim Laden der Seite aufgerufen. Dazu kommt das Ereignis window.onload zum Einsatz.
6
10.4.2 XMLHttpRequest kapseln 7
Noch etwas modularer ist es, den XMLHttpRequest-Aufruf komplett in einem Objekt zu kapseln. Diese Technik verwenden die meisten Ajax-Frameworks in der einen oder anderen Form. Als grundlegendes Beispiel kommt hier nicht nur eine einfache Textdatei zum Einsatz, sondern ein serverseitiges Skript, das die aktuelle Uhrzeit ausgibt. Damit haben Sie nämlich einen einfachen Wert, der sich kontinuierlich ändert. Hier ein Beispielskript in PHP:
8
9
Listing 10.4: Die Uhrzeit per PHP (Ausschnitt aus: uhrzeit.php) echo date('H:i:s');
10 Der Internet Explorer speichert das Ergebnis des Ajax-Aufrufs per GET in seinem Cache, sprich die Uhrzeit wird hier nicht aktualisiert, ohne dass der Nutzer den Browsercache löst. Dies lässt sich umgehen, indem Sie per serverseitige Technologie im HTTP-Header entsprechende Anweisungen vergeben. Einen Überblick über diese Problematik und die Lösungsmöglichkeiten finden Sie in Kapitel 13 »Problemlösungen« im Abschnitt 13.5 »CacheProbleme«.
INFO
11
12
13
327
Architektur und Grundlagen
Sehen wir uns nun die Bestandteile des Beispiels an: ■
Basis des Ganzen ist ein Objekt namens ArrAjax.8 var ArrAjax = new Object();
■
Dieses Objekt erhält eine Methode, die das XMLHttpRequest-Objekt erstellt und den eigentlichen Ajax-Aufruf absetzt: ArrAjax.aufruf = function(adresse, handler, methode) { //Ajax-Aufruf }
Die Methode besitzt drei Parameter: die URL (adresse) der Datei, die aufgerufen wird, eine Funktion als handler, die das Ergebnis des Aufrufs weiterverarbeitet, und die HTTP-Methode, also GET oder POST.
INFO
Der Einfachheit halber fehlen hier noch einige Möglichkeiten, beispielsweise die Übergabe von Parametern und die Wahl zwischen asynchroner und synchroner Übertragung. ■
Der übrige Code entspricht größtenteils dem schon bekannten Code für das Erstellen des XMLHttpRequest-Objekts und für das Absetzen des Ajax-Aufrufs. Einzig beim Event-Handler gibt es eine Besonderheit. Hier wird an onreadystatechange eine Funktion übergeben, die wiederum mit dem Ergebnis den vorher als Parameter angerufenen handler aufruft. xml_http.onreadystatechange = function() { if (xml_http.readyState == 4 && xml_http.status == 200) { handler(xml_http.responseText); } }
■
Dementsprechend muss es eine Funktion geben, die das Ergebnis übernimmt und ausgibt: function handler(ausgabe) { document.getElementById("ausgabe").innerHTML = ausgabe; }
■
Sie können nun in jedem Aufruf eine eigene Handler-Funktion und eine andere serverseitige Datei wählen. Im vorliegenden Beispiel wird bei einem Klick beispielsweise die Uhrzeit aufgerufen: Hier landet die Ajax-Ausgabe
Beim Laden dagegen der Text: window.onload = function() { ArrAjax.aufruf('daten.txt', handler, 'GET'); };
Die Handler-Funktion ist jeweils dieselbe, da die Rückgabe hier nur aus einfachem Text besteht. 8
328
Der Name ist selbstverständlich frei wählbar. Diese Minibibliothek heißt nach der Agentur, an der der Verfasser beteiligt ist.
XMLHttpRequest browserübergreifend
Hier der vollständige Code: Listing 10.5:
Ajax-Aufruf per Objekt (ajax_objekt.html)
Ajax-Ausgabe Hier landet die Ajax-Ausgabe
Abbildung 10.4: Die Uhrzeit wird aktualisiert.
10.5 Asynchron und synchron Von Ajax-Aufrufen gibt es zwei Varianten, asynchron und synchron. Welche Variante verwendet wird, steuern Sie in der Methode open() des XMLHttpRequest-Objekts. Steht der dritte Parameter auf false, wird ein synchroner Aufruf gestartet: xml_http.open(methode, adresse, false);
Der Standardwert ist dagegen asynchron bzw. true. Er wird in der Praxis in 95 % der Fälle verwendet. Ein asynchroner Aufruf erfolgt im Hintergrund. Das heißt, das übrige Skript läuft einfach weiter. Sprich, wenn Sie nach dem Senden des Aufrufs per send() weiteren JavaScript-Code eingefügt haben, wird er sofort ausgeführt. Auch andere Aufrufe werden gleichzeitig bearbeitet. Die Rückgabe wird dann beim Ereignis onreadystate verarbeitet.
330
Asynchron und synchron
Der synchrone Aufruf wird dagegen zuerst abgearbeitet. Nach dem Senden des Aufrufs mit send() hält das Skript an. Dementsprechend benötigen Sie auch onreadystate nicht, sondern können die Rückgabe direkt nach dem Senden verarbeiten: Listing 10.6: Synchroner Aufruf (ajax_synchron.html) xml_http.send(null); if (xml_http.readyState == 4) { handler(xml_http.responseText); }
Internet Explorer und Opera erlauben auch bei synchronem Datenaustausch einen Event-Handler bei onreadystate. Firefox und Mozilla ignorieren diesen Event-Handler. Das heißt für die Praxis, dass Sie die Verarbeitung von synchronen Aufrufen immer direkt an die send()-Methode anschließen sollten.
1
2
3
INFO
4
Nun zu einem Beispiel, das den Unterschied zwischen synchronen und asynchronen Aufrufen sehr gut zeigt. Sie benötigen zuerst ein serverseitiges Skript, das in der Abarbeitung länger dauert. Statt aber komplexe Datenbankzugriffe zu verwenden, können Sie das serverseitige Skript auch einfach kurz anhalten. In PHP funktioniert das mit der Funktion sleep(Sekunden). Sie erhält die Zahl der Sekunden als Parameter, die das Skript warten soll: Listing 10.7:
5
6
Ausschnitt aus uhrzeit_sleep.php
sleep(5); echo date('H:i:s');
7
Damit der Unterschied sichtbar wird, kommen zwei Aufrufe zum Einsatz. Der erste ruft das Skript mit Nachtruhe, der zweite das normale Skript, das sofort ausgeführt wird:
8
window.onload = function() { ArrAjax.aufruf('uhrzeit_sleep.php', handler_sleep, 'GET'); ArrAjax.aufruf('uhrzeit.php', handler, 'GET'); };
9
Für beide ist nun eine Ausgabe notwendig. Deswegen benötigen Sie zwei -Bereiche:
10
Hier landet die Ajax-Ausgabe Hier landet die Sleep-Ajax-Ausgabe
11
Und dementsprechend auch zwei Event-Handler: function handler(ausgabe) { document.getElementById("ausgabe").innerHTML = ausgabe; } function handler_sleep(ausgabe) { document.getElementById("ausgabe_sleep").innerHTML = ausgabe; }
12
13
Zum Schluss müssen Sie nur noch den dritten Parameter der open()Methode auf false setzen, damit synchron kommuniziert wird, und den 331
Architektur und Grundlagen
Code für die Verarbeitung der Rückgabe nach der send()-Methode einfügen. Hier der vollständige Quellcode: Listing 10.8: Ein synchroner Ajax-Aufruf (vereinfachte Variante von ajax_synchron.html) Ajax-Ausgabe Hier landet die Ajax-Ausgabe Hier landet die Sleep-Ajax-Ausgabe
Abbildung 10.5: Synchron: Bei der synchronen Ajax-Abfrage erscheinen beide Ergebnisse zeitversetzt, da die schlafende Abfrage zuerst ausgeführt wurde.
332
Steuerung und Rückgabe
Bei der Abarbeitung von synchronen Abfragen verhalten sich die Browser leicht unterschiedlich. Firefox, Mozilla und Internet Explorer zeigen die normalen HTML-Seiteninhalte noch nicht, der Opera dagegen schon, wenn der Ajax-Aufruf wie hier bei onload erfolgt. Der Hintergrund ist, dass die Browser ihre Anzeige gegebenenfalls erst aktualisieren, wenn kein JavaScript mehr ausgeführt wird, um mehrfache Aktualisierung zu vermeiden.
TIPP
1
Wenn Sie das Skript nun wieder auf asynchrone Übertragung einstellen, sehen Sie sehr schön den Unterschied: Bei der asynchronen Übertragung erscheint zuerst das Datum für den »nicht schlafenden« Ajax-Aufruf, danach das Datum für den schlafenden. Die Uhrzeiten sind dementsprechend um fünf Sekunden zeitversetzt.
2
3 Abbildung 10.6: Asynchron: Zuerst wird die schnellere Abfrage ausgeführt, auch wenn sie im Code als Zweites aufgerufen wird, …
4
5
6
Abbildung 10.7: … dann erscheint die zweite Uhrzeit um fünf Sekunden zeitversetzt.
7
8
9
10 10.6 Steuerung und Rückgabe
11
In diesem Abschnitt geht es um die versteckten Talente des XMLHttpRequestObjekts. Dazu gehört die Abfrage des Status der aktuellen Ajax-Anfrage, die Antworttypen und die Header. Ein paar spezielle Probleme in Sachen Timing und Steuerung von Ajax-Aufrufen werden in Kapitel 13 näher erläutert. Dort finden Sie auch eine Erläuterung zur Methode abort(), die einen Ajax-Aufruf abbricht.
12
13 INFO
333
Architektur und Grundlagen
10.6.1 Status Wenn die Ajax-Anfrage abgesendet wurde, wartet der JavaScript-Interpreter dank onreadystatechange oder dank des synchronen Ansatzes so lange, bis sich der Status der Anfrage verändert. Dieser Abarbeitungsstatus wird mit der Eigenschaft readyState ausgelesen. Er kennt verschiedene Werte, wie die folgende Tabelle zeigt. Tabelle 10.2: Werte für readyState9
9
Wert
Beschreibung
0
Noch nicht initialisiert (uninitialized). Dieser Wert ist vorhanden, wenn das XMLHttpRequestObjekt instanziiert, aber open() noch nicht aufgerufen wurde.
1
Laden (loading). Dieser Wert tritt ein, wenn open() aufgerufen wurde, send() aber noch nicht.
2
Geladen (loaded). Anfrage ist erfolgt, aber noch keine Daten empfangen. Im Firefox/Mozilla sind hier schon Headerinformationen verfügbar (siehe Abschnitt 10.6.3 »Header, Seite 336«).
3
Interaktiv (interactive). Einige Daten wurden geladen. Der Internet Explorer enthält noch keine Nachrichtenteile, der Mozilla/Firefox schon. Headerinformationen wurden bereits gesendet.
4
Vollständig (completed). Die Ajax-Antwort ist vollständig eingegangen.
Die verschiedenen Status können Sie ganz einfach testen, indem Sie (bei asynchroner Nutzung) im onreadystate-Event-Handler den Status und den Antworttext ausgeben: Listing 10.9: Die Abfrage von readyState (Ausschnitt aus: ajax_readyState.html) xml_http.onreadystatechange = function() { handler(xml_http.readyState + " " + xml_http.responseText + ""); }
Die Behandlung in den Browsern ist wie in der Tabelle schon erwähnt leicht unterschiedlich. Der Firefox erhält schon bei readyState 3 Text aus der Eigenschaft responseText. Der Internet Explorer liefert dagegen wegen dieser frühzeitigen Abfrage einen Fehler. In der Praxis werden Sie allerdings in den meisten Fällen nur den Status 4 abfragen und verwenden. Der readyState ist der Status der Ajax-Anfrage. Daneben gibt es noch die Eigenschaften status und statusText. Sie liefern die Rückmeldung der HTTP-Anfrage, die hinter jeder Ajax-Anfrage steckt.
9
334
Das W3C (World Wide Web Consortium) arbeitet im Moment an einem Standard für das XMLHttpRequest-Objekt. Dort sind dieselben Status definiert, allerdings sind die Begrifflichkeiten etwas anders, als man es in den Browsern gewohnt ist. So wird erst Status 4 als loaded bezeichnet (http://www. w3.org/TR/XMLHttpRequest/#xmlhttprequest-members).
Steuerung und Rückgabe
Abbildung 10.8: Der Firefox liest schon Inhalte beim readyState 3.
1
2
Abbildung 10.9: Der Internet Explorer kennt responseText erst beim readyState 4.
3
4
5
6
7
8
9
Im Normalfall ist das der status 200 und der statusText OK, wenn die Anfrage problemlos funktioniert hat. Wenn nicht, gibt es diverse Codes und Meldungen, die alle in der HTTP-Spezifikation (http://www.ietf.org/rfc/ rfc2626.txt) definiert sind. So können Sie beispielsweise darauf reagieren, wenn bestimmte Fehler eintreten, oder dem Nutzer wie in unserem Beispiel aus Listing 10.5 nur dann eine andere Ausgabe bieten, wenn die HTTPKommunikation erfolgreich war. Code
Text
Bedeutung
200
OK
Anfrage erfolgreich.
301
Moved Permanently
Die angefragte Ressource wurde permanent verschoben.
302
Found
Die angefragte Ressource wurde temporär verschoben.
400
Bad Request
Anfrage unverständlich.
10 11 Tabelle 10.3: Wichtige HTTP-Codes
12
13
335
Architektur und Grundlagen
Tabelle 10.3: Wichtige HTTP-Codes (Forts.)
TIPP
Code
Text
Bedeutung
401
Unauthorized
Unauthorisierte Anfrage.
403
Forbidden
Zugriff untersagt.
404
Not Found
Seite nicht gefunden.
500
Internal Server Error
Interner Serverfehler
503
Service Unavailable
Server vorübergehend nicht verfügbar.
Sie sollten für Überprüfungen nur den Statuscode mit status verwenden, nicht aber die Texte, denn manche Webserver verwenden für die Fehlertexte andere Varianten.
10.6.2 Antworttypen und Austauschformate Die Rückgabe eines Ajax-Aufrufs kennt zwei gängige Formate: ■
■
responseText liefert einen String. Wenn mehrere Elemente geschickt werden sollen, hat sich JSON als Alternative etabliert. JSON ist eine Notation für JavaScript-Objekte und -Arrays, die mit der eval()-Funktion in ein JavaScript-Objekt oder einen -Array umgewandelt werden kann. Mehr zu JSON in Kapitel 12. responseXML liefert XML zurück. Die Verarbeitung erfolgt dann per JavaScript. Mehr zur Arbeit mit XML lesen Sie in Kapitel 11.
Neben diesen Formaten bietet der Internet Explorer noch spezielle Formate: ■ ■
responseBody liefert den Inhalt der Ajax-Antwort als Bytes. responseStream ist auf die direkte Programmierung mit MSXML beschränkt. Hier wird ein Datenstrom geliefert.
Beide sind hier nur der Vollständigkeit halber aufgeführt und in der Praxis unrelevant. INFO
10.6.3 Header HTTP ist die Basis aller Ajax-Aufrufe und -Antworten. Dementsprechend haben Sie auch über das XMLHttpRequest-Objekt Zugriff auf die darunter liegenden HTTP-Header. Insgesamt vier Methoden stehen zur Verfügung: ■
336
getAllResponseHeaders() liest alle Felder des HTTP-Headers der jeweiligen Ajax-Antwort aus. Auch hier gibt es leichte Browserunterschiede: der Firefox/Mozilla liefert die Header schon ab readyState 2. Der Internet Explorer quittiert jede Abfrage vor dem Status 4 mit einem Fehler und liefert außerdem einige Felder nicht korrekt mit. Der Opera liefert zwar alle Felder, aber ebenfalls erst ab Status 4.
Steuerung und Rückgabe
Abbildung 10.10: Die Header der Ajax-Antwort (ajax_header. html)
1
2 Abbildung 10.11: Der Internet Explorer (hier Version 7) begnügt sich mit einer Teillieferung.
3
4
5
■
6
getResponseHeader(Feld) liefert den Wert eines einzelnen HTTP-Felds. Ein Feld ist eine einzelne Einstellung im HTTP-Header. Sie können beispielsweise nur den Content-Type abfragen:
7
xml_http.getResponseHeader("Content-Type") ■
setRequestHeader(Feld, Wert) dient dazu, ein HTTP-Feld zu setzen. Dies muss nach der open()-Methode geschehen. Einige Felder wie Connection und Keep-Alive dürfen hier allerdings aus Sicherheitsgründen nicht gesetzt werden. Setzbar ist aber beispielsweise das Feld UserAgent, das den Browser identifiziert:
8
9
xml_http.setRequestHeader("User-Agent", "MeinAgent");
Abbildung 10.12: Der entsprechende Header wurde geändert, wie Firebug10 hier zeigt.
10 11
12
13
10
10 Mehr zum Debugging erfahren Sie in Kapitel 29 »Debugging«.
337
Architektur und Grundlagen
WWW
Im Download-Archiv finden Sie zu den drei Methoden für den Header eine Beispieldatei namens ajax_header.html. Entfernen Sie einfach den Kommentar vor der jeweiligen Zeile, um die verschiedenen Methoden zu testen. ■
Die auf den Mozilla/Firefox beschränkte Methode overrideMimeType(Typ) hilft, wenn der Server einen falschen MIME-Typ11 für seine Antwort schickt. Dies geschieht beispielsweise, wenn eine XML-Datei mit einer falschen Dateiendung versehen ist, die der Webserver nicht dem korrekten MIME-Typ zuordnen. Dann würden Sie Folgendes angeben, um XML zu erhalten: xml_http.overrideMimeType("text/xml");
Soll dagegen XML als Text eingelesen werden, erreichen Sie das so: xml_http.overrideMimeType("text/plain");
Eine gute Übersicht über MIME-Typen und die jeweiligen Dateiendungen bietet http://www.webmaster-toolkit.com/mime-types.shtml. TIPP
10.7
Werte an den Server übermitteln
Bisher wurden nur Antworten verarbeitet, aber noch keine Werte per Ajax an den Server übermittelt. Das soll sich nun ändern. Beim Übermitteln von Werten steht am Anfang die Wahl der Versandmethode. Die zwei Standardversandmethoden von HTTP stehen hier zur Wahl: ■
■
GET verschickt die Daten nach einem Fragezeichen (?) an die URL angehängt. Die Namen der Formularelemente (name) und die Werte (value) werden paarweise durch Et-Zeichen (&) getrennt hintereinander gehängt. GET hat den Nachteil, dass die Zahl der Zeichen je nach Browser bzw. Webserver beschränkt ist. Spätestens bei 2.000 Zeichen ist Schluss. POST sendet die Daten im HTTP-Header, unter den übrigen HTTP-Feldern. Die Formatierung entspricht der bei GET mit Name/Werte-Paaren, die durch Et-Zeichen voneinander getrennt sind.
Um die Daten per GET an die per Ajax aufgerufene serverseitige Datei zu übermitteln, müssen Sie die Daten einfach nur als Parameter an die Adresse anhängen, die Sie in der Methode open() angeben. Bei POST läuft es etwas anders. Sie übergeben hier die Daten in der Methode send() als einzigen Parameter. Außerdem müssen Sie mit der aus dem letzten Abschnitt bekannten Methode setRequestHeader() den Content-Type auf das für POST notwendige application/x-www-form-urlencoded setzen, damit serverseitige Technologien die Daten verarbeiten können: xml_http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 11 Der MIME-Typ gibt den Datentyp an, der vom Server geliefert wird. MIME steht für Multipurpose Internet Mail Extension. Ursprünglich nur für E-Mails gedacht, wird er mittlerweile überall eingesetzt. Grundlage sind die Standards der IETF, beginnend bei RFC 2045 bis RFC 2049 (http://tools.ietf. org/html/rfc2045 bis http://tools.ietf.org/html/rfc2049).
338
Werte an den Server übermitteln
Als Beispiel soll unsere kleine Ajax-Klasse ArrAjax nun um die Möglichkeit erweitert werden, Parameter zu übergeben und dabei frei zwischen POST und GET als Versandmethode zu wählen. Dafür benötigen Sie zuerst eine serverseitige Datei, die zwei Parameter übernimmt. Die vorliegende PHP-Datei erlaubt POST- und GET-Parameter und bildet aus den zwei Parametern die Summe, die dann ausgegeben wird: 1
Listing 10.10: Ausschnitt aus: summe.php $summe = $_REQUEST['var1'] + $_REQUEST['var2']; echo $summe;
Daten, die per Ajax übermittelt werden, sind eine potenzielle Sicherheitslücke, denn jeder Nutzer mit Web-Know-how kann einen GET- oder POST-Aufruf selbst erstellen. Das heißt also, dass Sie die Daten, die Sie als Parameter in die serverseitige Datei übernehmen, auf jeden Fall validieren müssen, wenn Sie sie erhalten und bevor Sie sie beispielsweise wieder ausgeben oder in eine Datenbank schreiben.
2
3
TIPP
4
Nun wird die Methode aufruf des ArrAjax-Objekts um die Parameter erweitert. Die Parameter werden als Objekt übergeben. Ein möglicher Aufruf könnte so aussehen:
5
var parameter = new Object(); parameter.var1 = "2"; parameter.var2 = "5"; ArrAjax.aufruf('summe.php', handler, 'POST', parameter);
6
7
In der Methode aufruf wird dann aus dem parameter-Objekt der entsprechende String zusammengesetzt. Je nach Versandmethode wird er entweder an die Adresse angefügt oder mit der Variablen koerper an die send()-Methode übergeben. Für POST wird außerdem noch der Content-Type gesetzt:
8
if (parameter != null) { var parameter_string = ""; for (var ele in parameter) { parameter_string += ele + "=" + parameter[ele] + "&"; } parameter_string = parameter_string.substring (0,parameter_string.length - 1);
9
10 11
if (methode == "GET") { adresse += "?" + parameter_string; xml_http.open(methode, adresse, true); } else if (methode == "POST") { koerper = parameter_string; xml_http.open(methode, adresse, true); xml_http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); }
12
13
}
339
Architektur und Grundlagen
Hier im Überblick der vollständige Code. Die entscheidenden Stellen sind hervorgehoben: Listing 10.11: Parameter per GET und POST im Ajax-Aufruf übergeben (ajax_parameter.html) Ajax-Parameter Hier landet die Ajax-Ausgabe
9
10 Abbildung 10.13: Egal ob mit GET oder POST, die Summe wird korrekt berechnet.
11
12
13
341
Inhalt
11
XML 1
Das X in Ajax steht nicht umsonst für XML. Mit responseXML können Sie sich per Ajax ein XML-Dokument übergeben lassen und dies weiterverarbeiten. Gut geeignet ist diese Methode beispielsweise, wenn Sie umfangreiche strukturierte Daten verarbeiten müssen.
2
11.1
4
3
XML-Basics
XML steht für eXtensible Markup Language. Dabei handelt es sich um eine vom W3C1 standardisierte Sprache, mit der andere Sprachen definiert werden. Der Fachbegriff dafür ist eine Metasprache. XML definiert im Kern nur, dass die aus ihr definierten Sprachen aus Befehlen, so genannten Tags, bestehen und diese Tags Attribute haben können.
5
6
Da XML eine übergeordnete Sprache ist, gibt es natürlich viele Technologien, die davon abgeleitet sind: ■
■
■
■
■
7
DTD (DocType Definition) ist eine Sprache, um die Struktur von XMLDokumenten zu definieren. Auch für XHTML gibt es eine DTD, die die Struktur vorgibt. DTD ist selbst keine auf XML basierende Sprache. XSD (XML Schema Definition) erledigt dieselben Aufgaben wie die DTD, ist allerdings selbst eine XML-Sprache und kennt mehr Datentypen. XSL:FO (eXtensible Stylesheet Language:Formating Objects) ist eine XML-Sprache, um XML-Dokumente in druckbare Dokumente zu verwandeln. Der Standard wird heute meist zur PDF-Produktion verwendet. XPath (XML Path Language) ist eine Sprache, mit der in XML Dokumenten nach Elementen oder Daten gesucht werden kann. Sie findet z.B. innerhalb von XSLT Verwendung. XSLT (eXtensible Stylesheet Language Transformation) ist eine Sprache zur Transformation von XML-Dokumenten. Sie ist selbst auch eine auf XML basierende Sprache.
8
9
10
11 12
13
XML gibt es in Version 1.0 (http://www.w3.org/TR/xml/) und Version 1.1, wobei sich die beiden Versionen vor allem in der Unterstützung für verschiedene Zeichensätze unterscheiden.
343
ndex
1
XML
■
■
RSS (Really Simple Syndication2) ist ein XML-Format, das Nachrichten und Inhalte in einem standardisierten Format vorhält. Dank des einheitlichen Standards können diese Inhalte dann z.B. mit einem RSS-Reader abonniert werden und jeder Webblog-Betreiber kann seine Inhalte als RSS-Feed zur Verfügung stellen. SVG (Scalable Vector Graphics) ist eine auf XML basierende Sprache für Vektorgrafiken und Animationen.
Ein XML-Dokument ist streng hierarchisch aufgebaut. Es beginnt mit einer XML-Deklaration:
Die Deklaration kann die Version und den Zeichensatz des XML-Dokuments enthalten.
TIPP
Der Zeichensatz ist ein Ansatz zu vielen Problemen und Missverständnissen. Die beiden gebräuchlichsten Zeichensätze sind UTF-8 und ISO-8859-1. UTF-8 kodiert sämtliche Unicode-Zeichen in mehreren Bytes (bis zu vier). ISO-8859-1 verwendet dagegen nur ein Byte pro Zeichen, beherrscht dafür aber nur 255 Standardzeichen. Leider schreibt nicht jeder Editor korrekte UTF-8-Dokumente und nicht jede Skriptsprache kann UTF-8-Daten verarbeiten oder liefern. Deswegen sind hier in der Praxis häufiger Schwierigkeiten anzutreffen. Andererseits ist UTF-8 ein Muss, wenn man Daten und Texte außerhalb von Amerika oder Mitteleuropa verarbeiten will. Anschließend folgt das übergeordnete Wurzelelement. Darunter sind weitere Elemente (Tags) angeordnet. Die Tags wiederum können Inhalte erhalten und Attribute besitzen. Hier ein einfaches Beispiel, das auch in den folgenden Abschnitten zum Einsatz kommt: Listing 11.1:
Ein XML-Beispiel (pflanzen.xml)
Geranie Vollsonnig Rhododendron Freilandbedingungen Primel Bei Sonne schattieren
2
344
Die Bedeutung des Akronyms RSS wechselte im Lauf der Versionen. In der aktuellen Version 2 steht es für Really Simple Syndication.
XML und Ajax
Für den Einsatz mit Ajax gibt es keine Regeln, wie Ihr XML-Dokument auszusehen hat und welche Elemente enthalten sind. Diese Struktur (die Sie z.B. mit einer DTD oder XSD auch festschreiben können) geben Sie selbst vor und Sie schreiben auch den JavaScript-Code, um das Dokument dann weiterzuverarbeiten.
INFO
1
11.2
XML und Ajax 2
Der Ajax-Aufruf ändert sich kaum, wenn Sie XML statt normalem Text einsetzen. Ausgangspunkt für dieses Beispiel ist die Ajax-Klasse ArrAjax aus dem vorangegangenen Kapitel. Sie finden die Ausgangsdatei unter dem Namen ajax_parameter.html im Download-Archiv. Zweiter Bestandteil des Beispiels ist eine XML-Datei mit nur einem einzigen Wurzelelement als Inhalt: Listing 11.2:
3
4
Ein sehr einfaches XML-Dokument (daten.xml)
XML-Ausgabe
5
Sie können auf die XML-Objekte direkt mit responseXML zugreifen. Sollte die Klasse wie im aus dem letzten Kapitel schon bekannten Beispiel sowohl XML- als auch Text-Rückgaben verarbeiten können, haben Sie die Möglichkeit, den MIME-Typ der Antwort abzufragen und, nur wenn es sich um XML handelt, auf responseXML zu setzen:
6
7
var typ = xml_http.getResponseHeader("Content-Type"); if (typ == "text/xml" || typ == "application/xml") { handler(xml_http.responseXML); } else { handler(xml_http.responseText); }
Voraussetzung für die Verfügbarkeit der zurückgelieferten Daten als XMLObjekte ist, dass der Webserver die XML-Datei mit dem MIME-Typ text/xml oder application/xml ausliefert. Ist das nicht der Fall, sollten Sie prüfen, welchen MIME-Typ der Server verwendet, und diesen verwenden oder Sie ändern den MIME-Typ in den Webserver-Einstellungen. Wenn Sie das XML mit einer serverseitigen Technologie generieren, setzen Sie den MIME-Typ im HTT-Header-Attribut Content-Type. Hier ein entsprechendes Beispiel in PHP: Listing 11.3:
8
9 TIPP
10
11
Einfache XML-Ausgabe per PHP (daten.php)
12
13
345
XML
Handelt es sich nicht um XML, werden die Daten einfach als String eingelesen. Alles Weitere geschieht in der Handler-Funktion. Sie erhält die XMLDaten, die hier noch nicht weiterverarbeitet, sondern einfach ausgegeben werden. Listing 11.4:
XML per Ajax (ajax_xml.html)
function handler(xml) { document.getElementById("ausgabe").innerHTML = xml; }
Bei der Ausgabe im Browser wird nur der Datentyp XMLDocument angezeigt. Das liegt daran, dass die XML-Daten als Objekt-Baum vorliegen und wir hier nur das Wurzelobjekt zuweisen. Wenn Sie hier den Text aus dem XMLDokument sehen, dann wurde das XML fälschlicherweise als String übertragen. An dieser Stelle haken die nächsten Abschnitte ein und zeigen, wie Sie die XML-Daten in JavaScript weiterverarbeiten können. INFO
Abbildung 11.1: Die Ausgabe zeigt, dass es sich um ein XMLDocumentObjekt handelt.
11.3
XML in JavaScript
Die XML-Verarbeitung in JavaScript besteht aus verschiedenen Teilen: ■ ■
■
Dem Laden des XML-Dokuments. Dies ist leider je nach Browser ein wenig unterschiedlich, spielt allerdings für Ajax selbst keine Rolle. Dem XML-DOM, das ähnlich wie das schon bekannte HTML-DOM den Zugriff auf einzelne Elemente (Knoten) im XML-Dokument erlaubt. Damit wird meistens die Weiterverarbeitung von Ajax-Antworten vorgenommen. Der Umwandlung von XML per XSLT beispielsweise in HTML. Auch hier gibt es große Browserunterschiede.
In den folgenden Abschnitten lernen Sie alle drei Bestandteile kennen.
346
XML in JavaScript
11.3.1 XML laden und erstellen Wenn Sie eine Ajax-Antwort erhalten, ist das XML schon »da«, das heißt, Sie müssen sich um das Laden eines XML-Dokuments nicht mehr kümmern. Dies übernehmen der Ajax-Aufruf und responseXML. Ein wenig anders ist das, wenn Sie ein XML-Dokument direkt aus JavaScript aufrufen und mit dem XML-DOM bearbeiten möchten. Hier bieten die verschiedenen Browser unterschiedliche Wege, die hier kurz vorgestellt werden sollen: ■ ■
1
Der Internet Explorer verwendet ein ActiveX-Objekt namens Microsoft.XMLDOM. Mozilla, Firefox und Opera verwenden document.implementation und eine native XML-Implementierung. Ursprünglich stammt die Idee dazu aus dem Mozilla-Projekt. Der Opera hat das ab Version 9 übernommen.
2
3
Zentral für jedes Praxisbeispiel eine Fallunterscheidung, die zwischen ActiveX und nativer Implementierung unterscheidet. Hier zuerst der ActiveXPart:
4
if (window.ActiveXObject) { xml_dokument = new ActiveXObject("Microsoft.XMLDOM"); xml_dokument.async = false; xml_dokument.load("daten.xml"); handler(); }
5
6
Der Event-Handler wird hier manuell aufgerufen und der Aufruf erfolgt synchron. Die Mozilla-Implementierung verwendet dagegen onload.
7
else if (document.implementation && document.implementation.createDocument) { xml_dokument = document.implementation.createDocument("","",null); xml_dokument.load("daten.xml"); xml_dokument.onload = handler; }
8
Der Event-Handler selbst liest nur noch per XML-DOM (siehe nächster Absatz) einen Wert aus:
9
function handler() { var wert = xml_dokument.documentElement.firstChild.nodeValue; document.getElementById("ausgabe").innerHTML = wert; }
10
11
Hier der vollständige Code: Listing 11.5:
Ein XML-Dokument laden (xml_laden.html)
XML laden Hier landet die Ausgabe
Abbildung 11.2: Die XML-Ausgabe
TIPP
Neben dem Laden können Sie mit diesen Wegen auch ein XML-Dokument aus einem String erstellen. Dazu verwenden Sie im Internet Explorer einfach nur statt der Methode load() die Methode loadXML(). xml_dokument = new ActiveXObject("Microsoft.XMLDOM"); xml_dokument.async = false; xml_dokument.loadXML(xml_string);
Für Firefox, Opera und Konsorten gibt es das DOMParser-Objekt: var parser = new DOMParser(); xml_dokument = parser.parseFromString(xml_string,"text/xml"); handler();
Ein Beispiel finden Sie im Download-Archiv unter dem Namen xml_erstellen. html. 348
XML in JavaScript
11.3.2 XML-DOM Das Document Object Model kennen Sie schon für normale HTML- und XHTML-Seiten. Eine XML-Datei ist natürlich ebenso dafür prädestiniert, denn es handelt sich auch hier nur um strukturierte Daten. Für das XML-DOM gelten dementsprechend dieselben Regeln, Eigenschaften, Methoden und KnotenArten, wie Sie sie schon in Kapitel 8 und dort besonders im Abschnitt »W3CDOM« kennen gelernt haben. Deswegen geht es an dieser Stelle nur um die Besonderheiten von XML-Dokumenten und um entsprechende Beispiele. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Aus anderen Technologien kennen Sie vielleicht alternative Möglichkeiten, XML zu parsen. Dazu zählen der SAX-Zugriff oder die aus Microsoft .NET bekannten XMLReader und XMLWriter. Für JavaScript gibt es diese Möglichkeiten leider nicht. Eine Ausnahme sind die proprietären Implementierungen wie die MSXML-Bibliothek, die per ActiveX-Objekte in den Internet Explorer integriert wird. Sie enthält teilweise noch andere XML-Verarbeitungsmöglichkeiten wie eine SAX2-Schnittstelle. Und auch der Mozilla kann in XML noch mehr bieten. Hier gibt es mit XUL eine eigene Sprache für die Browseroberfläche (http://developer.mozilla.org/en/docs/XUL). Diese Alternativen werden hier allerdings nicht behandelt, da sie durch ihren browserspezifischen Fokus nur für sehr wenig Entwickler überhaupt in Frage kommen. Die einzige browserübergreifende Alternative zum DOM ist XSLT (siehe Punkt 11.3.3 »XSLT mit JavaScript«).
1
2
Tabelle 11.1: XML-DOM
3
4 INFO
5
6
7
8 Der wichtigste Unterschied zwischen dem HTML-DOM und dem XML-DOM ist, dass Sie nicht per document auf das aktuelle Dokument zugreifen, sondern direkt das XML-Dokument verwenden. Zwei Beispiele, jeweils eines für den Direktzugriff und eines über den Zugriff per Knoten-Hierarchie, zeigen dies. Basis der beiden Beispiele ist die folgende XML-Datei mit den (abgespeckten) Pflegeanleitungen für bekannte Blumen: Listing 11.6:
9
10
XML-Beispiel (pflanzen.xml)
Geranie Vollsonnig Rhododendron Freilandbedingungen Primel
11 12
13
349
XML
Bei Sonne schattieren
Diese Liste soll nun per Ajax eingelesen und in eine HTML-Tabelle überführt werden. Direktzugriff Der Direktzugriff geht bekanntermaßen über zwei Wege: ■ ■
getElementById(ID) mit Zugriff über das ID-Attribut. getElementsByTagName(Name) mit Zugriff über den Namen des jeweiligen Tags.
Im vorliegenden Beispiel bietet sich eher der Gang über den Tag-Namen an, da ja alle Pflanzen ausgelesen werden sollen. Das Ergebnis ist dann eine NodeList, eine Liste mit Knoten.
TIPP
Der Zugriff über den Tag-Namen hat seine Tücken, wenn sich Tags mit demselben Namen auf verschiedenen Hierarchiestufen des Dokuments befinden, denn er nimmt alle Tags mit dem passenden Namen ungeachtet der Hierarchie in die NodeList auf. Die Verarbeitung des XML-Codes erfolgt vollständig im Event-Handler. Die notwendige Ajax-Verbindung basiert auf dem Skript aus Abschnitt 11.2 »XML und Ajax«. Geliefert wird ein XML-Dokument. Die wichtigsten Bestandteile für den Direktzugriff im Event-Handler sind: ■
Das Auslesen der Tags und . Es erfolgt mit getElementsByTagName() und liefert eine NodeList: var gattung_knoten = xml.getElementsByTagName("gattung"); var licht_knoten = xml.getElementsByTagName("licht");
■
Eine Schleife, die eine der beiden Knoten-Listen durchgeht: for (var i = 0; i < gattung_knoten.length; i++) { }
Wir gehen hier davon aus, dass ein -Tag sowohl als auch enthält, sodass die Knoten-Listen für Gattung und Lichtverhältnisse jeweils gleich lang sind.
TIPP
In der Praxis sollten Sie viel Zeit darauf verwenden, Ihre Daten in XML sinnvoll zu strukturieren und mit DTD oder XML-Schema die Struktur gut zu beschreiben. Dann haben Sie es später beim Programmieren mit JavaScript und Ajax wesentlich leichter. ■ ■
350
Die Ausgabe von Tabellen-Tags. Sie werden hier in der Variablen ausgabe gespeichert. Und schließlich den Zugriff auf den Inhalt der Gattungs- und Licht-Knoten. Der Zugriff erfolgt über firstChild und dann nodeValue, denn beim
XML in JavaScript
Inhalt handelt es sich um einen Text-Knoten, der sich unter dem eigentlichen Element befindet: ausgabe += gattung_knoten[i].firstChild.nodeValue;
Hier der vollständige Code inklusive ein wenig CSS, um die Tabelle zu formatieren: Listing 11.7:
XML-Ausgabe per Direktzugriff (ajax_xml_direktzugriff.html)
1
Ajax und XML table { border: solid 1px black; border-collapse: collapse; } th, td { border: solid 1px black; padding: 5px; }
12
13
351
XML
Hier landet die Ajax-Ausgabe
Abbildung 11.3: Die fertige Tabelle
Zugriff über Knoten-Hierarchie Der Ausgangspunkt für den Zugriff über die Knoten-Hierarchie ist immer das Wurzelelement. Sie sprechen es mit der Eigenschaft documentElement an. Bei unserem pflanzen.xml-Dokument könnten Sie nun per Schleife alle pflanze-Knoten durchgehen und dann auf die untergeordneten Knoten gattung und licht zugreifen. Vereinfacht könnte ein solcher Zugriff für das erste Element so aussehen: wurzel.childNodes[0].firstChild.firstChild.nodeValue; wurzel.childNodes[0].lastChild.firstChild.nodeValue;
Könnte, weil diese Art von Zugriff mit unserer XML-Vorlage so nicht funktioniert. Das Problem ist, dass das XML-Dokument Leerzeichen und Zeilenumbrüche enthält – so genannte Whitespaces. Diese Whitespaces werden im DOM des Browsers als eigene Text-Knoten behandelt. Leider gibt es auch keine Möglichkeit, diese Whitespace-Knoten zu ignorieren.3 Die einfachste Möglichkeit, das zu verhindern, besteht darin, dass Sie aus dem XML-Dokument sämtliche Zeilenumbrüche, Leerzeichen und Tabs entfernen. Viele XML-Editoren erledigen das teilweise schon automatisch. Das produzierte XML ist allerdings für Menschen nicht mehr allzu angenehm zu lesen.
3
352
Andere DOM-Implementierungen z.B. in ASP.NET oder PHP bieten solche Möglichkeiten.
XML in JavaScript
Listing 11.8:
XML ohne Whitespace (pflanzen_ohne_ws.xml)
GeranieVollsonnig Rhododendron FreilandbedingungenPrimel Bei Sonne schattieren
1
Eine andere flexiblere Möglichkeit, um das Problem zu vermeiden, besteht darin, den Knoten-Typ mit nodeType zu testen. Wie in Kapitel 8 nachzulesen ist, steht der Typ 1 für ein Element, der Typ 3 dagegen für Text. Sie können nun in einer Schleife einfach eine Fallunterscheidung einbauen, die nur für Element-Knoten gilt:
2
for (var i = 0; i < wurzel.childNodes.length; i++) { if (wurzel.childNodes[i].nodeType == 1) { //Zugriff auf die untergeordneten Elemente } }
3
4
Für die DOM-Methoden firstChild() und lastChild() schreiben Sie dann einfach eigene Alternativen, die so lange suchen, bis sie einen Element-Knoten gefunden haben. Hier das Beispiel für firstChild():
5
ArrAjax.arrFirstChild = function(ele_start) { ele_ende = ele_start.firstChild; while(ele_ende && (ele_ende.nodeType != ele_start.nodeType)) { ele_ende = ele_ende.nextSibling; } return ele_ende; };
6
7
Zuerst definieren Sie das Element ele_ende als firstChild von dem als Parameter übergebenen ele_start. Solange das Element existiert und nicht denselben Knoten-Typ hat wie das Startelement (also kein Element-Knoten ist), wird der Geschwister-Knoten verwendet. Sobald der Knoten den Typ Element besitzt, wird ele_ende zurückgeliefert.
8
9
Die Hilfsfunktionen für lastChild(), nextSibling(), previousSibling() etc. funktionieren nach demselben Muster.
10 INFO
Hier ein vollständiges Beispiel, das die Inhalte von pflanzen.xml als HTMLTabelle ausgibt: Listing 11.9:
11
Ajax-Zugriff per Knoten-Hierarchie (ajax_xml_knoten.html)
Ajax und XML table { border: solid 1px black; border-collapse: collapse; }
354
XML in JavaScript
th, td { border: solid 1px black; padding: 5px; } Hier landet die Ajax-Ausgabe
1
Abbildung 11.4: Dasselbe Ergebnis, anderer Weg: Per Hierarchie lassen sich XML-Dokumente ebenfalls auslesen.
2
3
4
5
6
Mischformen
7
In der Praxis ist es manchmal schwierig, zu entscheiden, ob dem Direktzugriff oder der Knoten-Hierarchie der Vorzug gegeben werden soll. Eindeutig ist die Entscheidung nur, wenn per getElementById() auf ein ganz bestimmtes Element zugegriffen werden kann.
8
Dementsprechend sind am häufigsten Mischformen zwischen Direktzugriff und Hierarchiezugriff anzutreffen. Mit getElementsByTagName() wird beispielsweise ein übergeordnetes Tag angesprochen und dann werden die einzelnen Knoten per firstChild, nextSibling etc. durchgegangen.
9
10
Egal, welche Form Sie verwenden: Sie sollten darauf achten, dass sich Ihr XML-Dokument nicht nachträglich ändert. Strukturänderungen und Änderungen von Namen sind nachträglich ausgesprochen problematisch und werfen oft aufwändige Programmierarbeit über den Haufen.
11 12
11.3.3 XSLT mit JavaScript XSLT steht für eXtensible Stylesheet Transformations (http://www.w3.org/ TR/xslt.html). Bei XSLT handelt es sich an sich um ein XML-Dokument. Es enthält vorgegebene XSLT-Tags, die dafür sorgen, dass ein zugrunde liegendes XML-Dokument in ein anderes Format transformiert wird. Bei den Zielformaten unterscheidet man die Transformation in eine andere XML-
13
355
XML
Sprache oder ein anderes XML-Format (Seitwärtstransformation) und die Transformation in ein Ausgabeformat (Abwärtstransformation). Damit die Transformation eines XML-Dokuments per XSLT durchgeführt werden kann, ist ein so genannter XSLT-Parser notwendig. Damit diese Transformation über JavaScript gesteuert werden kann, muss er natürlich im Browser integriert sein. Tabelle 11.2: XSLT
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Für das folgende Beispiel kommt dieses einfache XSLT-Dokument zum Einsatz. Es erzeugt eine HTML-Tabelle mit den Informationen zu den Pflanzen als einzelnen Zeilen: Listing 11.10: Ein einfaches XSLT (pflanzen_transform.xslt)
| |
In den Browsern gibt es leider wie schon gewohnt zwei völlig unterschiedliche Ansätze, um mit XSLT umzugehen. Der Internet Explorer setzt – auch in Version 7 – auf ActiveX. Basis sind ein XSLT-Dokument und ein TemplateDokument: var xslt = new ActiveXObject("MSXML2.FreeThreadedDOMDocument"); xslt.async = false; xslt.load("pflanzen_transform.xslt"); var template = new ActiveXObject("MSXML2.XSLTemplate"); template.stylesheet = xslt;
Dem Template wird das XSLT zugewiesen.
356
XML in JavaScript
Die eigentliche Transformation übernimmt dann der XSLT-Prozessor: var prozessor = template.createProcessor(); prozessor.input = xml; prozessor.transform();
Im Mozilla4 und Opera wird mit document.implementation und dort der Methode createDocument() ein XML-Dokument erzeugt. In dieses Dokument laden Sie das XSLT.
1
var xslt = document.implementation.createDocument("", "", null); xslt.async = false; xslt.load("pflanzen_transform.xslt");
2
Für die Transformation ist dann das XSLTProcessor-Objekt zuständig. var prozessor = new XSLTProcessor(); prozessor.importStylesheet(xslt); var fragment = prozessor.transformToFragment(xml, document);
3
Entsprechend unterschiedlich sind auch die Rückgaben. Der MicrosoftXSLT-Prozessor liefert einen String, der Mozilla-Bolide dagegen ein Dokumentfragment (oder alternativ mit transformToDocument() ein ganzes Dokument). Ersteres kann einfach mit innerHTML ausgegeben, Letzteres muss als Kind-Knoten mit appendChild() angehängt werden.
4
Hier die vollständige handler()-Funktion, die zwischen Microsoft und Mozilla/Opera unterscheidet:
6
5
Listing 11.11: XSLT und Ajax (Ausschnitt aus: ajax_xslt.html)
7
function handler(xml) { if (window.ActiveX) { var xslt = new ActiveXObject("MSXML2.FreeThreadedDOMDocument"); xslt.async = false; xslt.load("pflanzen_transform.xslt"); var template = new ActiveXObject("MSXML2.XSLTemplate"); template.stylesheet = xslt; var prozessor = template.createProcessor(); prozessor.input = xml; prozessor.transform(); document.getElementById("ausgabe").innerHTML = prozessor.output; } else if (window.XSLTProcessor) { var xslt = document.implementation.createDocument("", "", null); xslt.async = false; xslt.load("pflanzen_transform.xslt"); var prozessor = new XSLTProcessor(); prozessor.importStylesheet(xslt); var fragment = prozessor.transformToFragment(xml, document); document.getElementById("ausgabe").innerHTML = ""; document.getElementById("ausgabe").appendChild(fragment); } } 4
8
9
10
11 12
13
Die offizielle Beschreibung finden Sie unter http://developer.mozilla.org/en/docs/Using_the_ Mozilla_JavaScript_interface_to_XSL_Transformations.
357
XML
11.3.4 XPath mit JavaScript XPath ist eine Hilfstechnologie aus dem XML-Universum. Sie ist eigentlich dazu gedacht, in XSLT und anderen Technologien auf Knoten zuzugreifen. Sie kann allerdings für den Knoten-Zugriff auch eigenständig eingesetzt werden. Allerdings hat dieser Direkteinsatz seinen Preis: Zum einen schließt er den Opera aus, zum anderen sind die Ansätze in Internet Explorer auf der einen und Mozilla auf der anderen Seite extrem unterschiedlich. Tabelle 11.3: XPath
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Basis dieses Beispiels ist der XPath-Ausdruck: var xpath_ausdruck = "//gattung";
Er liefert alle Knoten an beliebige Stellen, die den Namen gattung tragen. Der Internet Explorer bietet zwei Methoden: selectNodes(XPath-Ausdruck) und selectSingleNode(XPath-Ausdruck). Die zwei Methoden können auf jedem beliebigen Knoten im XML-DOM ausgeführt werden, z.B. im Wurzelelement: xml.documentElement.selectNodes(xpath_ausdruck);
Die Rückgabe ist eine Liste mit Knoten, die Sie dann per Schleife durchgehen können. Auf die Inhalte gelangen Sie hier wieder über das DOM: for (var i = 0; i < xpath_erg.length; i++) { ausgabe += xpath_erg[i].firstChild.nodeValue + ""; }
Der Mozilla geht einen deutlich komplizierteren Weg.5 Der XPath-Ausdruck wird mit document.evaluate(XPath-Ausdruck, Kontext, Namespace, Typ, ) aufgerufen. Hier stehen nun fünf Parameter zur Wahl, von denen drei absolut entscheidend sind. Der erste ist der XPath-Ausdruck. Der zweite ist der Kontext: Das kann ein XML-Dokument oder ein bestimmter Knoten sein, von dem ausgehend der XPath-Ausdruck sucht. Der dritte Parameter lässt sich einsetzen, wenn das XML-Dokument Namespaces enthält. Sie können damit die Namensraum-Präfixe filtern. Wenn nicht benötigt, bleibt die Einstellung null. Der dritte Parameter steuert den Rückgabewert. Sie können hier einfache Datentypen wie beispielsweise einen String wählen (XPathResult.STRING_TYPE), wenn Sie nur ein Ergebnis mit diesem Datentyp erwarten. Sind mehrere Ergebnisse zu erwarten, bietet sich ein Iterator an: var xpath_erg = document.evaluate(xpath_ausdruck, xml, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
5
358
Die offizielle Beschreibung finden Sie unter http://developer.mozilla.org/en/docs/Introduction_ to_using_XPath_in_JavaScript.
XML in JavaScript
Der fünfte Parameter gibt an, ob ein bestehendes Ergebnis-Objekt als Basis verwendet werden soll oder ob ein neues erstellt wird. In unserem Fall geben wir mit null an, dass ein neues entsteht. Anschließend wird der Iterator durchgegangen: var knoten = xpath_erg.iterateNext(); while (knoten) { ausgabe += knoten.textContent + ""; knoten = xpath_erg.iterateNext(); }
1
2
Der Einfachheit halber haben wir hier die Fehlerprüfung mit try-catch weggelassen, da das Ergebnis feststeht. Hier ein Überblick über die komplette Event-Handler-Funktion. Die Fallunterscheidung ist hervorgehoben:
3
Listing 11.12: Auslesen per XPath (Ausschnitt aus: ajax_xpath.html) function handler(xml) { var ausgabe = ""; var xpath_ausdruck = "//gattung"; if (window.ActiveXObject) { var xpath_erg = xml.documentElement.selectNodes(xpath_ausdruck); for (var i = 0; i < xpath_erg.length; i++) { ausgabe += xpath_erg[i].firstChild.nodeValue + ""; } } else if (document.evaluate) { var xpath_erg = document.evaluate(xpath_ausdruck, xml, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); var knoten = xpath_erg.iterateNext(); while (knoten) { ausgabe += knoten.textContent + ""; knoten = xpath_erg.iterateNext(); } } document.getElementById("ausgabe").innerHTML = ausgabe; }
4
5
6
7
8
9 Abbildung 11.5: Die Inhalte der gattung-Tags werden ausgegeben.
10
11 12
13
359
XML
11.4
E4X
Seien wir ehrlich, der DOM-Zugriff auf XML-Dokumente ist nicht gerade intuitiv oder einfach. Das wurde auch dem Standardisierungsgremium ECMA klar. Die ECMA steht hinter ECMAScript, dem Standard für den Sprachkern von JavaScript (und auch anderer Skriptsprachen wie ActionScript). Dementsprechend begann die Entwicklung an einem alternativen XML-Zugriff namens E4X (ECMA for XML). Er basierte auf einem Ansatz der Firma BEA. Der Standard steht unter http://www.ecma-international. org/publications/standards/Ecma-357.htm zur Verfügung. Abbildung 11.6: Die Spezifikation für E4X
Den Standard selbst gibt es seit Dezember 2005. Leider lässt die Browserimplementierung bisher zu wünschen übrig. Hauptsächlich das Mozilla-Projekt hat sich E4X angenommen. So unterstützen sowohl der Firefox ab Version 1.5 als auch Rhino6, die JavaScript-Engine für Java, den Standard zumindest teilweise. Leider hat Microsoft E4X nicht in den Internet Explorer 7 integriert. 6
360
Hier finden Sie Rhino: http://www.mozilla.org/rhino/.
E4X
Trotzdem ist E4X eine sehr interessante Technologie, da sie den Zugriff auf XML-Knoten sehr einfach macht. Sie erstellen ein XML-Element einfach mit dem Konstruktor: var xml_objekt = new XML();
Optional können Sie gleich einen XML-Knoten übergeben. Der Zugriff auf Elemente erfolgt einfach per Eigenschaft. Zurückgeliefert wird der Inhalt des jeweiligen Knotens. Auf Attribute greifen Sie mit vorangestelltem At-Zeichen (@) zu.
1
2
Im folgenden Beispiel wird ein Teil von pflanzen.xml aus diesem Kapitel als Knoten eingelesen und werden die entsprechenden Daten ausgegeben: Listing 11.13: E4X im Einsatz (e4x.html)
3
E4X Hier landet die Ajax-Ausgabe
4
5
6
7
8
Abbildung 11.7: E4X erlaubt einfachen Zugriff auf Werte von Attributen und Elementen.
9
10
11 12
13
361
Inhalt
12 JSON 1
2
Das Entlanghangeln am XML-DOM-Baum ist nicht jedermanns Sache und in vielen Projekten auch schlicht überdimensioniert oder zu weit entfernt von der normalen Programmierung. Deswegen hat man sich im Zuge von Ajax auf eine JavaScript-Funktionalität namens JSON zurückbesonnen. JSON steht für JavaScript Object Notation. Dabei handelt es sich um eine Kurzform, in die Sie Arrays und Objekte schreiben können. Sie ist aus Kapitel 6 über JavaScript-Objekte bereits bekannt.
3
4
Der Name JSON wurde durch die Website http://www.json.org/ bekannt gemacht. Allerdings handelt es sich glücklicherweise um keine proprietäre Erweiterung, sondern um einen Teil des Sprachkerns, der sogar in ECMAScript festgelegt ist und in allen Browsern funktioniert. M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 12.1: JSON funktioniert sogar im Netscape Navigator 4.x
In den folgenden Abschnitten lernen Sie zuerst die JSON-Schreibweise kennen. Anschließend geht es um den Datenaustausch per Ajax mit Hilfe von JSON.
■ ■
7
8
JSON ist allerdings bei weitem keine komplette Serialisierung aller Programmierobjekte. Und sie besitzt auch nicht alle Fähigkeiten von XML-Daten. Es fehlen zum Beispiel: ■
6
9
Schema-Daten, die die Struktur vorgeben Metadaten – d.h. beschreibende Daten Eine klare Unterscheidung zwischen Attributen und Werten
10
11
12 13
363
ndex
NS4.x
5
JSON
Abbildung 12.1: Die ursprüngliche JSON-Website
12.1
Arrays
Arrays werden in JSON einfach in eckigen Klammern notiert. Die Elemente sind dann durch Kommata getrennt. Sind die Werte Strings, stehen sie in Anführungszeichen: var array = ["Wert1", "Wert2"];
Hier ein Beispiel mit den Wochentagen: var tage = ["Montag", "Dienstag", "Mittwoch"];
Auf das Array kann dann wie auf jedes andere Array auch zugegriffen werden. Die folgende Zeile gibt beispielsweise Dienstag aus: Listing 12.1:
Ein Array per JSON (json_arrays.html)
alert(tage[1]);
364
Arrays
Abbildung 12.2: Der Zugriff klappt
1
2
JSON unterstützt dieselben Datentypen wie JavaScript selbst: ■ ■ ■ ■ ■ ■
String Number Object Array Boolean, sprich true und false null
INFO
3
4
Strings werden mit doppelten Anführungszeichen geschrieben. Einfache Anführungszeichen sind zwar auch möglich, aber die doppelten Anführungszeichen werden hier z.B. von http://www.json.org/ als Standard angesehen. Und auch für das Entwerten von Sonderzeichen gilt das JavaScript-Prinzip mit vorangestelltem Backslash.
5
6
Bei Zahlen gelten ebenfalls die normalen JavaScript-Konditionen. Beispielsweise sind auch Fließkommazahlen und negative Zahlen im JSON-Code problemlos möglich.
7
Das Date-Objekt wird nicht unterstützt. Datumswerte werden im ISO-Format angegeben, z.B. »1978-04-18T13:20:00«.
8
Arrays lassen sich per JSON auch verschachteln. Daraus entstehen dann dementsprechend multidimensionale Arrays. Das folgende Beispiel erzeugt zwei Arrays mit jeweils »Montag« und »Dienstag«:
9
var wochen = [["Montag", "Dienstag"], ["Montag", "Dienstag"]]; alert(wochen[1][0]);
10
Der Zugriff erfolgt wie gewohnt per Index. Der erste Index wählt das zweite Array in dem multidimensionalen Array wochen, der zweite Index dann das erste Element, sprich den »Montag«.
11 Abbildung 12.3: Auch verschachtelte Arrays lassen sich per JSON erzeugen und dann auslesen.
365
12 13
JSON
TIPP
Assoziative Arrays sind aus Sicht von JSON Objekte mit Name/Wert-Paaren. Sie unterliegen dementsprechend der Syntax von Objekten, die Sie im nächsten Abschnitt kennen lernen.
12.2
Objekte
Objekte sind aus JSON-Sicht vor allem Name/Wert-Paare. Begrenzt wird das Objekt von geschweiften Klammern, getrennt werden Name und Wert durch Doppelpunkt. Zwischen den Paaren steht ein Komma: var objekt = { Name: "Wert", Name2: "Wert2" };
Der Zugriff erfolgt dann per Punktsyntax wie bei jedem anderen Objekt auch. Hier ein kleines Beispiel: Listing 12.2:
Objekte in JSON (json_objekt.html)
var tage = { Montag: "Monday", Dienstag: "Thuesday" }; alert(tage.Montag);
Nach dem letzten Name/Wert-Paar darf kein Komma stehen. Der normalerweise als tolerant bekannte Internet Explorer stört sich daran. TIPP
Abbildung 12.4: Der übersetzte Wochentag
Komplex wird diese Syntax, wenn Sie mehrere Objekte verschachteln oder in Objekten Arrays verschachteln. Das folgende Beispiel zeigt ein Array Tage in einem Objekt Datum. Dort ist allerdings noch eine Besonderheit zu sehen: eine Methode im Objekt. Sie wird per function definiert. Der Zugriff auf das Array erfolgt per this: Listing 12.3:
Ein JSON-Objekt mit Array und Methode (json_objekt_verschachtelt.html)
var Datum = { Tage: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"], WochentagAusgeben: function() {
366
JSON und Ajax
var heute = new Date(); alert("Heute: " + this.Tage[heute.getDay()]); } }; Datum.WochentagAusgeben();
Abbildung 12.5: Heute ist Mittwoch
1
2
3
12.3
JSON und Ajax
4
Um JSON zum Datenaustausch in Ajax einzusetzen, sind nur wenige Schritte nötig. Sie müssen im serverseitigen Code JSON produzieren und diesen dann im clientseitigen Code mit responseText übernehmen. Der JSON-Code wird also als String übertragen. Dementsprechend muss er wieder in JavaScript umgewandelt werden. Dies geschieht mit der Funktion eval(). Sie müssen einfach nur die JSON-Rückgabe einem Objekt zuweisen:
5
6
eval("var objekt = " + json + ";");
Natürlich ist es auch denkbar, JSON in die andere Richtung zu verwenden und damit JavaScript-Objekte an den Server zu übermitteln. Bei allem, was Sie hier entwickeln, sollten Sie allerdings immer an die Sicherheit denken. Schließlich bieten Sie im Web Schnittstellen, an die jeder Nutzer weltweit Daten schicken kann. Übernehmen Sie diese Daten einfach ungeprüft direkt in Ihren Programmcode, besteht die Möglichkeit, dass böswillige Nutzer eigenen Code einschleusen. Mehr dazu in Kapitel 30 »Sicherheit«. Auch einige Frameworks helfen hier, beispielsweise filtert Prototype mit seiner JSON-Serialisierung auf Wunsch (siehe Kapitel 26).
7 TIPP
8
9
10
Ausgangsbasis für einen JSON-Aufruf ist eine serverseitige Datei, die JSON als Ausgabe produziert. In diesem einfachen Beispiel reicht es, wenn Sie eine Textdatei auf Ihren Webserver legen und den JSON-Code einfügen: Listing 12.4:
11
Eine einfache Textdatei mit JSON (pflanzen.txt)
12
[ {gattung: "Geranie", licht: "Vollsonnig"}, {gattung: "Rhododendron", licht: "Freilandbedingungen"}, {gattung: "Primel", licht: "Bei Sonne schattieren"}
13
]
367
JSON
TIPP
In der Praxis ist es natürlich sinnvoll, Objekte und Arrays aus einer serverseitigen Technologie in JSON zu übersetzen. So kann quasi nahtlos kommuniziert werden. Das Umwandeln in JSON kann auch als Serialisieren1 bezeichnet werden, das Rückumwandeln als Deserialisieren. Beides übernehmen in vielen Fällen die Ajax-Frameworks. Für die meisten serverseitigen Technologien gibt es außerdem Bibliotheken, die bei der Übersetzung der Objekte in die JSON-Notation helfen. Im Ajax-Teil können Sie das bereits bekannte Ajax-Objekt ArrAjax einsetzen. window.onload = function() { ArrAjax.aufruf('pflanzen.txt', handler, 'GET', null); };
Die im letzten Kapitel zu XML gezeigte Unterscheidung nach MIME-Typen funktioniert auch hier, da JSON in einer Textdatei enthalten ist. Bei einer serverseitigen Technologie müssen Sie den MIME-Typ unter Umständen selbst setzen. xml_http.onreadystatechange = function() { if (xml_http.readyState == 4 && xml_http.status == 200) { var typ = xml_http.getResponseHeader("Content-Type"); if (typ == "text/xml" || typ == "application/xml") { handler(xml_http.responseXML); } else { handler(xml_http.responseText); } } }
Nachdem der String per responseText angekommen ist, wird er im Handler weiterverarbeitet. Hier geschieht zweierlei. Zuerst wird mit eval() das Objekt aus dem JSON-String produziert. Ein try-catch-Konstrukt hilft, eventuelle Fehler zu vermeiden: function handler(json) { try { eval("var pflanzen = " + json + ";"); } catch (e) { }
Ist das Objekt erstellt, prüfen Sie am besten, ob es existiert, und auch, ob es der gewünschte Datentyp ist. Hier gibt es natürlich die Möglichkeit, noch beliebig genau zu prüfen, welche Daten übermittelt wurden. Die Ausgabe wird dann mit einer einfachen Schleife erzeugt, die die einzelnen Elemente aus dem Array durchgeht und auf die Eigenschaften gattung und licht zugreift: 1
368
Serialisieren bezeichnet als Begriff an sich das Umwandeln in einen String oder in ein anderes Format, das die Daten sequenziell, sprich der Reihe nach, enthält.
JSON und Ajax
Listing 12.5:
JSON-Abfrage per Ajax (ajax_json.html)
if (pflanzen != null && typeof(pflanzen) == "object") { var ausgabe = "
Art | Licht |
"; for (var i = 0; i < pflanzen.length; i++) { ausgabe += ""; ausgabe += pflanzen[i].gattung; ausgabe += " | "; ausgabe += pflanzen[i].licht; ausgabe += " |
"; } ausgabe += "
";
1
2
document.getElementById("ausgabe").innerHTML = ausgabe;
3
} }
Abbildung 12.6: Die Ausgabe per Tabelle
4
5
6
7
8
9
10
11
12 13
369
Inhalt
13 Problemlösungen 1
2
Im Prinzip ist ein Ajax-Aufruf sehr einfach. Allerdings treten rund um den einfachen Aufruf einige Schwierigkeiten auf, die Sie auf jeden Fall bedenken und unter Umständen auch lösen sollten.
13.1
3
Status und History 4
Das Webprinzip ist sehr einfach: Der Nutzer folgt Links und wechselt von Seite zu Seite. Jede Seite hat einen eindeutigen Identifikator, einen URI (Unique Resource Identifier). Die Adresse lässt sich problemlos in den Browser eintippen und der Browser selbst besitzt Lesezeichen, um Webseiten zu speichern und die Vor- und Zurück-Schaltflächen, um zwischen den Seiten zu navigieren.
5
6
Nun denken Sie aber an eine Ajax-Anwendung: Hier bleibt die Seite immer gleich. Neue Daten werden vom Server ohne Neuladen geholt. Das heißt auch, dass sich der Status der Anwendung zwar ändert, nicht aber der Status der Seite. Das heißt also z.B. in einer Ajax-Formularanwendung, dass der Nutzer schon das dritte Formular sieht, ohne dass sich der URI der Seite geändert hätte.
7
8
Das hat natürlich Auswirkungen auf die Nutzung der Seite. Wenn der Nutzer ein Bookmark setzt, zeigt es nur auf den Einstieg, sprich auf das erste Formular. Noch schlimmer wirkt aber, dass die Vor- und Zurück-Schaltfläche nicht funktioniert. Möchte der Nutzer vom dritten zum zweiten Formular zurück, klappt das nicht, er landet vielmehr bei der vorher besuchten Website.
9
10
Der Lösungsansatz ist klar: Notwendig ist eine Möglichkeit, um den Status der Anwendung festzustellen, der aber nicht zum Neuladen führt. Damit fallen URL-Parameter im so genannten Query-String hinter dem Fragezeichen weg, da sie ein Neuladen verursachen. Es bleibt der Hash (auch Doppelkreuz) für einen HTML-Anker, der das Springen in der Seite erlaubt:
11
12
http://www.xyz.de/index.html#status1
Natürlich sollte für die hier beschriebene Lösung kein Ziel für den jeweiligen Anker in der Seite sein, da der Browser sonst dorthin springt. Eine Ausnahme sind sehr lange Seiten. Ist der Anker dort nicht bekannt, springt der Browser an den Anfang der Seite – auch nicht immer ein erwünschtes Verhalten.
371
ndex
13
Problemlösungen
INFO
Der hier beschriebene Ansatz wurde und wird von verschiedenen Entwicklern erdacht und verfeinert (z.B. http://contentwithstyle.co.uk/ Articles/38/fixing-the-back-button-and-enabling-bookmarking-for-ajaxapps/). Wer sich weniger um das Coding kümmern möchte, dem sei das Miniframework Really Simple History (RSH) empfohlen (http://www. codinginparadise.org/projects/dhtml_history/README.html). Es kapselt die History-Funktionalität in eine eigene API. Unter den Ajax-Frameworks gibt es mittlerweile einige, die History-Funktionalität unterstützen. Der Pionier war das Dojo Toolkit (http://dojotoolkit.org/). Um einen Hash in JavaScript zu setzen, verwenden Sie einfach die JavaScript-Eigenschaft location.hash: location.hash = "#" + escape("Hashwert");
TIPP
location.hash funktioniert im Prinzip in allen Browsern. Leider macht der Safari bzw. Konqueror einige Probleme: Zum einen wird der Hash oft mit Doppelkreuz ausgelesen, zum anderen klappt dort das Bookmarken nicht. Die Probleme sind so schwerwiegend, dass die Really Simple History (http://codinginparadise.org/weblog/2005/09/safari-no-dhtmlhistory-possible.html) und viele andere Frameworks auf Safari-/Konqueror-Kompatibilität verzichten. Wollen Sie im Safari- und Konqueror nicht auf die History verzichten, können Sie alternativ zum Hash unter Umständen auch einen Query-String einsetzen und das Neuladen in Kauf nehmen.
Ist der Hash erst mal gesetzt, können Sie ihn jederzeit auslesen: if (location.hash.length > 1) { var status = unescape(location.hash.substring(1)); }
TIPP
Der Hash-Wert kann von böswilligen Nutzern sehr leicht manipuliert werden. Das ist dann problematisch, wenn Sie den Wert ausgeben oder gar mit eval() ausführen. Damit haben Sie eine potenzielle Cross-Site-Scripting (XSS)- oder JavaScript-Inclusion-Lücke auf Ihrer Website geschaffen (siehe Kapitel 30). Eine weitere Einschränkung ist, dass die URL mit dem Hash nicht beliebig lang werden darf. Für die Lesezeichen reicht die Lösung mit dem Hash bereits aus. Sie wird von allen Browsern unterstützt, außer wie erwähnt von Safari und Konqueror. Für Vor- und Zurück benötigen Sie ein wenig mehr. Damit der jeweilige Schritt immer angezeigt bzw. die Ausgabe aktualisiert wird, benötigen Sie einen regelmäßigen Funktionsaufruf: window.onload = function() { ajax_aufruf(); window.setInterval("ajax_aufruf()", 200); }
372
Status und History
In der Funktion wird der Hash-Wert geprüft und dementsprechend das richtige serverseitige Skript aufgerufen. Eines enthält in diesem Beispiel die Uhrzeit, das andere das Datum. function ajax_aufruf() { status = ""; if (location.hash.length > 1) { status = unescape(location.hash.substring(1)); } if (status == "uhrzeit") { ArrAjax.aufruf('uhrzeit.php', handler, 'POST'); } else if (status == "datum") { ArrAjax.aufruf('datum.php', handler, 'POST'); } }
1
2
3
Nun geht es an die Statusänderung: Sie wird hier mit zwei Schaltflächen erreicht. Klickt der Nutzer darauf, wird die Funktion setze_status() aufgerufen und der Status als Parameter übergeben. Im Firefox bzw. Mozilla reicht das Setzen des Hash-Werts. Im Internet Explorer ist der Hash allerdings nicht für die History relevant. Hier muss der Wert also noch im QueryString von einem unsichtbaren iFrame gespeichert werden.
4
5
Hier sehen Sie den vollständigen Code: Listing 13.1:
6
Ajax-Aufruf mit History-Funktion (ajax_history.html)
Ajax-Parameter 1) { status = unescape(location.hash.substring(1)); } if (status == "uhrzeit") { ArrAjax.aufruf('uhrzeit.php', handler, 'POST'); } else if (status == "datum") { ArrAjax.aufruf('datum.php', handler, 'POST'); } } function setze_status(status) { location.hash = "#" + escape(status); if (window.ActiveXObject) {
11
12
13
373
Problemlösungen
window.frames["versteckt"].window.location.search = "?" + escape(status); } ajax_aufruf(); } window.onload = function() { ajax_aufruf(); window.setInterval("ajax_aufruf()", 200); } //--> Hier landet die Ajax-Ausgabe
Abbildung 13.1: Zwischen Uhrzeit und ...
Abbildung 13.2: … Datum kann nun auch per Browser-History gewechselt werden
374
Sicherheitsbeschränkungen
13.2
Sicherheitsbeschränkungen
Bei lokalen Dateien greift die Sicherheitseinstellung von Ajax, dass nur Daten von derselben Domain abgerufen werden können. Das heißt, Sie können nicht vom lokalen Webserver aus auf Dateien auf einem anderen Webserver beispielsweise bei einem Hoster zugreifen. Diese Sicherheitsmaßnahme ist Sache des Browsers.
1
Wenn Sie per Ajax beispielsweise einen Web Service auf einem anderen Webserver einbinden wollen, müssen Sie erst mit einer serverseitigen Technologie auf den Web Service zugreifen und dann können Sie die Daten über ihr eigenes Skript (eine Art Proxy) an die Ajax-Anwendung zur Verfügung stellen. Diese Methode verwenden auch einige spezialisierte Ajax-Bibliotheken. Ohne eigenes Skript können auch Funktionen des Webservers wie mod_proxy vom Apache eingesetzt werden.
2
3
Sie können die Sicherheitsbeschränkungen bereits mit lokalen Dateien testen. Greifen Sie dazu einfach aus einer lokal geöffneten Datei auf Ihren lokalen Webserver zu:
4
xml_http.open('GET', 'http://localhost/ajax_javascript/daten.txt', true);
5
Im Firefox erhalten Sie in der Konsole die Meldung, dass der Zugriff nicht erlaubt ist. Im Internet Explorer lassen sich lokale Dateien dagegen zum Funktionieren bringen, wenn Sie dem Ausführen von Steuerelementen zustimmen. Bei Dateien aus dem Web funktioniert das über Domaingrenzen hinweg natürlich nicht.
6
7 Abbildung 13.3: Domaingrenzen lassen sich nicht überwinden.
8
9
10 Mit Browserzertifikaten (vergleiche Kapitel 30) lässt sich die Sicherheitsbeschränkung umgehen. Allerdings muss der Nutzer dazu auch eine – auf viele Nutzer sehr beunruhigend wirkende – Nachfrage beantworten.
11
INFO
Eine andere Abhilfe ist, nicht auf XMLHttpRequest zu setzen. So einfach das klingt, mit klassischen GET-Anfragen, z.B. in einem versteckten Frame, lassen sich auch Daten von anderen Domains einlesen. Auch der Einsatz von Flash ist denkbar. Allerdings ist der Weg über einen serverseitigen Proxy – sei es ein Skript oder eine Webserver-Funktionalität – meistens wesentlich praktischer.
12
13
375
Problemlösungen
13.3
Accessibility
Accessibility oder auf Deutsch Barrierefreiheit bedeutet, dass eine Website möglichst keine Nutzer ausschließt. Dazu gehören ältere Nutzer, Nutzer mit Behinderung und auch Nutzer mit wenig Weberfahrung. Die auch in der deutschen BITV (Barrierefreie Informationstechnologie Verordnung) sowie in den österreichischen und schweizerischen Rechtsregelungen verankerten Grundregeln für Barrierefreiheit stammen vom W3C oder genauer der WAI, einer Arbeitsgruppe des W3C (http://www.w3.org/WAI/). Die zu Grunde liegenden Regeln heißen WCAG (Web Content Accessibility Guidelines). Aktuell ist Version 2.0 in Arbeit. Sieht man sich nun diese Regeln an und überlegt sich gleichzeitig, dass blinde Menschen mit einem Screenreader1 im Web unterwegs sind, sieht man, was an einer AjaxAnwendung problematisch ist: Der JavaScript-Code kann beispielsweise von einem Screenreader nicht interpretiert werden und natürlich kann sich der Nutzer nicht einfach den Code vorlesen lassen. Ein einfaches Beispiel, das das illustriert, ist eine Ajax-Suche.2 Der Nutzer gibt etwas in ein Textfeld ein – das Textfeld ist per Screenreader sehr einfach zu finden. Anschließend klickt er auf Senden oder drückt (Enter). Werden die Ergebnisse nun mit Ajax geladen und per JavaScript und z.B. innerHTML ausgegeben, hat der Nutzer eines Screenreaders Pech gehabt. Wichtig ist hier, eine Alternative zu bieten. Beispielsweise könnte es eine normale Schaltfläche geben, die ein serverseitiges Skript ausführt, das dann die Suche befüllt.3 Auf der anderen Seite kann JavaScript natürlich auch in Sachen Accessibility behilflich sein. Beispielsweise arbeiten die Schaltflächen zur Vergrößerung der Schrift oder zur Verbesserung des Kontrastes oftmals mit JavaScript-Code, der ein CSS manipuliert.
INFO
Bei Accessibility-Diskussionen geht es häufig um Sehbehinderungen, die ein Problem darstellen. Bunte Bilder und Animationen verwirren aber genauso auch ältere Menschen, Menschen mit Lernschwächen oder Jugendliche mit geringerer Konzentrationsfähigkeit. Auch hier kann eine moderne Anwendung zu schnell zu viel des Guten sein.
13.4
Timing
Die asynchronen Aufrufe von Ajax sind natürlich eine wunderbare Funktionalität. Man startet eine Abfrage, der Nutzer und das Skript können weiter-
1 2
3
376
Ein Screenreader liest eine Website vor. Eines der bekanntesten Produkte ist JAWS (http://www. freedomscientific.com/fs_products/software_jaws.asp). Microsoft hat sich mit seiner Live-Suche (http://www.live.com/) anfangs im Bemühen um AjaxFunktionalität ein wenig in die Nesseln gesetzt. Dort gab es eine Suche, die die Ergebnisse direkt per Ajax angezeigt hat und ohne Statusverwaltung auskam. Einen sehr guten Vorschlag bezüglich der Alternativversionen für eine Anwendung macht http://domscripting.com/blog/display/41.
Timing
arbeiten, und dann kommt die Antwort. Aus diesem Vorteil erwachsen allerdings auch einige Probleme mit dem Timing: ■ ■ ■ ■
Netzwerklatenz – der Nutzer erhält nicht sofort Feedback, da Daten über das Netz geschickt werden müssen. Serverprobleme – der Server schickt nach längerem Warten nur eine Fehlermeldung. Inaktivität – der Nutzer ist lange Zeit inaktiv. Dies löscht z.B. die Nutzersitzung. Konkurrierende und voneinander abhängige Requests erzeugen Probleme. Die Spezifikation beschränkt die Anzahl an Requests auf einen Server auf maximal zwei gleichzeitige Requests.
1
2
3
Die folgenden Abschnitte geben einen kurzen Überblick über die einzelnen Problempunkte und verraten Lösungsansätze.
4
13.4.1 Netzwerklatenz Das Netz ist nicht so schnell, wie man es gerne hätte. Auf dem Weg vom Client zum Server warten sehr viele Stationen, die alle einen HTTP-Aufruf um ein paar Millisekunden verlangsamen können. In vielen Fällen ist diese Netzwerklatenz, sprich die notwendige Wartezeit dank Verzögerungen im Netzwerk, kein Problem.4 Bei komplexeren Ajax-Anwendungen kann es allerdings schwierig werden. Nehmen Sie eine Anwendung wie Webmail: Der Nutzer klickt auf eine Schaltfläche zum Laden von Nachrichten. In einer klassischen Anwendung würde die Seite nun neu geladen – der Nutzer weiß, dass er warten muss. In einer Ajax-Anwendung drückt er dagegen auf den Knopf und bekommt kein Feedback vom Browser. Da sich die Anwendung aber immer mehr wie eine Desktopanwendung verhält, erwartet er direktes Feedback.
5
6
7
8
Das heißt, in diesem Fall müssen Sie als Entwickler ihm Feedback geben, damit er nicht wie ein Irrer auf die Senden- und Empfangen-Schaltfläche hämmert. Das Feedback kann aus einer Deaktivierung anderer Funktionen bestehen und natürlich auch eine Ladeanzeige enthalten.
9
10
Leider gibt es in Ajax keine Möglichkeit, browserübergreifend eine korrekte Ladeanzeige mit dem aktuellen Ladestatus anzuzeigen. Das Problem ist hier, dass ein Ajax-Aufruf keine Zwischenberichte gibt. Geeignet wäre theoretisch der readyState 3. In diesem Stadium liefern allerdings nur Mozilla und Firefox einen Teil der HTTP-Nachricht.
11
12
Das folgende Beispiel nutzt den readyState. Dazu erstellen Sie zuerst ein serverseitiges Skript, das die Inhalte schon frühzeitig ausgibt. Im vorliegenden Beispiel wird das mit der PHP-Methode flush() realisiert, bevor das Skript dann fünf Sekunden schlafen geschickt wird. 4
13
Hier hilft oft schon ein ping-Befehl in der Konsole oder ein einfacher Lasttest mit dem BenchmarkWerkzeug des Apache (http://httpd.apache.org/docs/2.0/programs/ab.html).
377
Problemlösungen
Listing 13.2:
Elemente teilweise ausgeben (sleep.php)
In der Abfrage wird zusätzlich der readyState mit dem Wert 3 abgefragt und über dieselbe Funktion ausgegeben. Da der Internet Explorer hier eine Fehlermeldung liefern würde, weil responseText und responseXML unbekannt sind, wird try-catch eingesetzt: Listing 13.3:
Status feststellen (Ausschnitt aus ajax_netzwerklatenz.html)
xml_http.open(methode, adresse, true); xml_http.onreadystatechange = function() { if (xml_http.readyState == 4 && xml_http.status == 200) { handler(xml_http.responseText); } else if (xml_http.readyState == 3) { try { handler(xml_http.responseText); } catch(e) { } }
Abbildung 13.4: Zuerst erscheint nur die Uhrzeit, ...
Abbildung 13.5: … dann auch das Datum.
378
Timing
Wenn Sie statt Text XML zurückliefern, ist das XML bei readyState 3 noch kein vollständiges XML-Dokument, sondern nur ein Text. Um das Einlesen müssen Sie sich in diesem Fall händisch kümmern.
TIPP
Request abbrechen Wenn ein Ajax-Request wegen der Netzwerklatenz (oder des Webservers) zu lange läuft, können Sie ihn auch abbrechen. Dafür zuständig ist die Methode abort() des XMLHttpRequest-Objekts.
1
Für das folgende Beispiel kommt die Datei sleep.php aus dem letzten Abschnitt wiederum als serverseitiges Skript zum Einsatz. Dieses Mal wird das Skript allerdings abgebrochen, wenn zwei Sekunden vergangen sind:
2
Listing 13.4:
3
Ajax abbrechen (ajax_abort.html)
var timer = 0; var id = window.setInterval(function () { timer++; if (timer > 2) { alert(timer); xml_http.abort(); window.clearInterval(id); } }, 1000);
4
5
6
Das Ergebnis ist, die Daten, die vor den zwei Sekunden Verzögerung ausgegeben wurden, sind sichtbar, sprich hier die Uhrzeit. Danach wird aber nichts mehr ausgegeben. Hätte das serverseitige Skript nicht (hier dank flush()) schon etwas ausgegeben, würde nichts zurückgegeben.
7
8 Abbildung 13.6: Nur die Zeit wird angezeigt, da sie vor dem Abbruch übermittelt wurde.
9
10
11
13.4.2 Serverprobleme
12
Beim Timing gibt es naturgemäß auch Probleme, wenn der Server nicht nur lange braucht, sondern selbst scheitert. Die einfachen Statusmeldungen wie 404 sind dabei relativ harmlos. Schafft der Server dagegen die Abarbeitung nicht, gibt es meist recht undefinierte Fehler der Klasse 500. Hier hilft nur, den Statuscode abzufangen und dann mühsam Fehler zu suchen (siehe Kapitel 29 »Debugging«).
13
379
Problemlösungen
Das Problem ist nur, dass die Fehlercodes natürlich nicht an den Nutzer weitergegeben werden sollten. Trotzdem sollten Sie als Website-Verantwortlicher darüber informiert werden, auch wenn die Site schon im normalen Betrieb läuft. Eine wirksame Anlaufstelle ist das Error-Log des Servers. Eine andere Idee ist, Fehler per Ajax-Request an ein anderes Skript weiterzuleiten und dort zu loggen. Dies scheitert natürlich, wenn der Server global nicht mehr ansprechbar ist.
13.4.3 Inaktivität Ein anderes Timing-Problem ist, wenn der Nutzer sehr lange inaktiv ist. Das Problem betrifft meist die Sessionmanagement-Funktionalität der serverseitigen Technologie. Eine Session wird über das Timeout meist nach einer festgelegten Zeit beendet. Funktioniert eine Ajax-Anwendung aber größtenteils autark und ohne Requests bzw. verlangt vom Nutzer nicht so häufige Eingriffe, wird der Timeout oft angenommen, obwohl das Browserfenster noch geöffnet ist. Abhilfe dafür schaffen Sie über regelmäßige Requests, die Sie per window.setInterval() oder window.setTimeout() absetzen. Das serverseitige Skript aktualisiert bei diesem Request einfach nur das Timeout der Sitzung.
13.4.4 Konkurrierende Requests Das clientseitige Timing ist bei asynchronen Requests nicht so wichtig wie bei synchronen Requests. Dennoch sollten Sie auf ein paar Dinge achten: ■
Beim Laden müssen Sie darauf achten, dass alle HTML-Elemente schon geladen sind, bevor darauf zugegriffen wird. Dies gilt natürlich auch für die Elemente, in denen die Ajax-Response ausgegeben wird, denn es ist immer möglich, dass der Ajax-Aufruf vor dem Laden der Seite beendet ist. Die Standardabhilfe ist das onload-Ereignis: window.onload = function() { }
■
■
380
onload berücksichtigt nur die HTML-Seite selbst, nicht externe JavaScript-Dateien, die in der Regel am Ende geladen werden. Deswegen ist beim Einsatz von externen JavaScript-Skripten eine Nachricht erforderlich, wenn sie übertragen sind. Die übliche Technik ist, in den externen JavaScript-Dateien eine Variable zu setzen, die regelmäßig mit window.setInterval() oder window.setTimeout() getestet wird. Sie sollten überlegen, ob Sie eher viele kleine Requests oder weniger große Requests absetzen. Dies hängt sowohl vom Webserver als auch von den übermittelten Daten ab. In der Praxis hat sich meist ein Mittelding bewährt.
Cacheprobleme
■
Sie sollten außerdem beachten, dass die Spezifikation offiziell nur zwei gleichzeitige Requests – ob voneinander abhängig oder nicht – erlaubt. Das heißt also, Daten müssen unter Umständen in einem Request zusammengepackt und z.B. als ein JSON-Element serialisiert werden.
13.5
Cacheprobleme
1
Ajax-Anwendungen sollen normalerweise regelmäßig aktualisiert werden. Dem steht unter Umständen der Browsercache entgegen. Firefox und Mozilla cachen XMLHttpRequest-Aufrufe allerdings normalerweise nicht, wohl aber der Internet Explorer. Er nimmt alle GET-Aufrufe (nicht POST) in den Cache auf. Sehen Sie sich dazu das folgende Beispiel an: Listing 13.5:
2
3
Regelmäßiger Aufruf (ajax_cache.html)
window.onload = function() { var timer = 0; var id = window.setInterval(function () { ArrAjax.aufruf('uhrzeit_cache.php', handler, 'GET'); }, 200); };
4
5
Hier wird das Skript uhrzeit_cache.php alle 200 Millisekunden per GET aufgerufen. Im Firefox sehen Sie die regelmäßige Aktualisierung, im Internet Explorer dagegen nicht. Zur Cachesteuerung gibt es nun einige HTTP-Header. Hier werden die entsprechenden Header in PHP gesetzt: Listing 13.6:
6
7
Cachesteuerung (uhrzeit_cache.php)
8
9
10
Entscheidend für den Internet Explorer ist die Angabe Expires, die den Inhalt an einem Datum in der Vergangenheit ablaufen lässt. Die anderen Header schaden allerdings nicht.
11
Zum Testen müssen Sie im Internet Explorer den Browserverlauf löschen, da sonst ja das Skript im Cache liegt.
12 TIPP
Neben dem Setzen der serverseitigen Header können Sie auch noch clientseitig gegen das Cachen vorgehen. Dazu müssen Sie für jeden Aufruf eine andere URL setzen. Dies geht, indem Sie einen Zufallswert an die URL anhängen.
13
381
Problemlösungen
Listing 13.7:
Caching vermeiden (ajax_cache_url.html)
window.onload = function() { var timer = 0; var id = window.setInterval(function () { var zufall = Math.floor(Math.random() * 10000000000); ArrAjax.aufruf('uhrzeit_cache.php?zufall=' + zufall, handler, 'GET'); }, 200); };
INFO
382
Der umgekehrte Fall ist natürlich auch möglich, nämlich dass Sie Caching haben möchten. Eine handgemachte Lösung könnte z.B. so aussehen, dass Sie das Ergebnis eines Ajax-Requests prüfen und wenn es dasselbe ist wie beim vorigen Request, verzögern Sie den nächsten Request entsprechend. Sollen sehr viele Daten übermittelt werden, ist es auch möglich, einen Request zu haben, der ein serverseitiges Skript aufruft, das nur zurückliefert, ob es Änderungen gibt. Nur dann werden alle Daten per zweiten Request aufgerufen.
Teil 3 Webanwendungen Dieser Teil setzt das Wissen über Syntax und Sprache von JavaScript voraus. Hier wird mit dem DOM-Modell gearbeitet, die Browserobjekte und Ajax kommen zum Praxiseinsatz und Sie finden viele praktische Anwendungen. Am Anfang dieses Teils stehen die Anwendungen im Vordergrund, die jeder professionelle JavaScript-Programmierer gerne einsetzt. Sie erstellen kleine Hilfen für die Nutzer, öffnen neue Fenster, greifen auf Frames zu und lernen, mit Cookies zu arbeiten. Später werden die Anwendungen komplexer. Mit Stylesheets können Sie per JavaScript das Aussehen der Seite beliebig ändern: Animierte Objekte fliegen durch die Luft und typische Web 2.0Effekte, wie Sie sie von den großen Websites kennen, werden erklärt.
385 417 451 477 511 527 553 597 621 639 683
Browserunterscheidung Bilder Navigationshilfen Fenster Cookies Frames Formulare Vollständigkeitsüberprüfung und reguläre Ausdrücke CSS und JavaScript Dynamisches Multimedia, Java etc.
14 15 16 17 18 19 20 21 22 23 24
Inhalt
14 Browserunterscheidung 14 15
Browserübergreifend zu programmieren stellt für den Webentwickler eine der größten Herausforderungen dar. Dieses Problem ist heute nicht mehr ganz so drängend wie noch vor ein paar Jahren. Auf Fossilien wie Netscape 2 und 3 oder Internet Explorer 3 muss keine Rücksicht mehr genommen werden. Die vierte Generation der großen Browser, Internet Explorer 4 und Netscape Navigator 4, gehören mittlerweile zu den noch etwas lebenden Fossilien. Sprich, man kann sie berücksichtigen oder man lässt es. Legt man noch Wert auf diese Browser, hat man allerdings eine Menge Probleme.1 Und wie ist es mit neueren Browsern? Schon beim Einsatz von Ajax ist eine Browserunterscheidung notwendig.2 Neben dieser immer wiederkehrenden Browserunterscheidung gibt es viele weitere JavaScript-Eigenschaften und -Methoden, bei denen sich auch die modernen Browser wie Firefox, Internet Explorer 7, Konqueror/Safari und Opera unterscheiden.
16
17
18
19
Was kann der motivierte Entwickler tun? Er muss sich bei jeder Methode, Eigenschaft oder Funktion, die er verwendet, Gedanken über die unterstützten Browser machen. Dabei hilft Ihnen dieses Buch, denn es bietet zu allen wichtigen Bereichen Übersichtstabellen mit den unterstützten Browsern.
20
21
Prinzipiell gilt: Je moderner eine Anwendung ist, desto eher können Sie beispielsweise auf die vierte Generation der Browser verzichten. Beispielsweise macht es für eine Ajax-Anwendung keinen Sinn, noch Netscape 4 zu unterstützen, der das XMLHttpRequest-Objekt nicht kennt. Alle, die für ein Intranet produzieren, werden sich jetzt freuen. Wenn es nur einen Browser gibt, hat man alles im Griff. Die Praxis zeigt allerdings anderes. In den meisten Intranets dürfen die Nutzer auch andere Browser installieren. Bei mir nicht, werden manche Systemadministratoren sagen. Aber: Insbesondere bei Laptops ist das kaum zu kontrollieren und viele Firmen mit Kopfarbeitern wollen den Mitarbeitern diese Freiheit auch nicht nehmen.
22
23 TIPP
24
Wer von der Norm abweicht, also andere Browser verwendet, hätte also schon mal Pech gehabt.3 Wird das in Kauf genommen, gibt es aber weitere Probleme. Neue Browser halten Einzug und vielleicht wird eines Tages in der
Die Unterschiede in den DOM-Modellen finden Sie in Kapitel 8 »Document Object Model«. Siehe Kapitel 10 »Architektur und Grundlagen«. Und wer sagt, dass alle in der Firma Windows verwenden? Für Linux-Nutzer gibt es beispielsweise keinen Internet Explorer, am Mac sehen die Browser durchaus ein wenig anders aus …
385
ndex
1 2 3
Browserunterscheidung
ganzen Firma der Browser gewechselt. Dann sind browserübergreifende Skripte meist schneller angepasst.4 Hat sich der Entwickler nach langem Hin und Her entschlossen, welche Browser er beliefern möchte, bleibt die Frage, welche Methode zur Browserunterscheidung am sinnvollsten ist. Zur Wahl stehen drei sehr unterschiedliche Methoden: ■ ■ ■
die Verwendung des language-Attributs, um die JavaScript-Version herauszufiltern die Informationen aus dem navigator-Objekt, um Browser und Version zu unterscheiden Browserobjekte, die bestimmte Browser identifizieren
Jede der drei Methoden wird hier besprochen und hat bestimmte Vor- und Nachteile. Sie finden nach der Vorstellung der Alternativen jeweils einen kurzen Absatz mit der Überschrift »Sinnvoll?«, der erläutert, für welche Anwendungen diese Art der Browserunterscheidung sinnvoll ist.
14.1
language-Attribut
Das language-Attribut haben Sie bereits in Kapitel 1 kennen gelernt. Wie Sie es zur Versionsunterscheidung verwenden, wird in Abschnitt 1.8 »Versionsunterscheidung« beschrieben. Wollen Sie mal nur den Internet Explorer ansprechen, verwenden Sie als language-Attribut einfach JScript. TIPP
14.1.1 Sinnvoll? An dieser Stelle steht die Diskussion im Mittelpunkt, inwieweit das language-Attribut zur Browserunterscheidung sinnvoll ist. Abschnitt 1.8 »Versionsunterscheidung« hat deutlich gezeigt, dass zum Beispiel der Internet Explorer 6 sich nur mit JavaScript 1.3 identifiziert. Daher ist natürlich in der Praxis diese JavaScript-Version das wählbare Maximum, da ansonsten ein Browser mit relevanten Marktanteilen ausgeschlossen würde. Bei den älteren Browsern schließen Sie damit einen Teil der vierten Generation – Netscape Navigator 4.0 bis 4.06 und Internet Explorer 4 – aus. Darauf könnte man allerdings wohl verzichten. JavaScript 1.2 hat einige Tücken. Sieht der Netscape Navigator 4.x diesen Wert für das language-Attribut, verwendet er seine eigene Interpretation von einigen Funktionen und Methoden. Beispielsweise funktioniert der
4
386
In vielen Firmennetzwerken war dies beim Wechsel vom Netscape Navigator 4.x auf den Internet Explorer der Fall. Dieser Wechsel war qualitativ und technisch oft notwendig, änderte aber für den JavaScript-Programmierer alles.
Deaktiviertes JavaScript
Genau gleich-Operator (===) exakt wie der Gleichheitsoperator (==). Weitere Probleme treten bei String- und Array-Methoden auf. Die Ursache dafür lag darin, dass die Netscape-Verbesserungen an JavaScript vor der ECMAScript-Standardisierung erfolgten. Daher entschlossen sich die Netscape-Programmierer, die eigenen Besonderheiten nur dann wirken zu lassen, wenn der Programmierer Version 1.2 explizit im language-Attribut aufruft. Aufgrund der Abwärtskompatibilität behielt man dieses Verhalten bei. In der Praxis gilt also die Empfehlung, nicht mit JavaScript 1.2, sondern mit einer höheren Version zu arbeiten. Wie bei allen Problemen gibt es auch hier Lösungen: Sie könnten in einem Skriptbereich mit JavaScript 1.2 nur eine Variable einführen, die besagt, dass der Browser JavaScript 1.2-kompatibel ist, und diese Variable dann in einer Fallunterscheidung in einem JavaScript 1.1-Bereich verwenden. Der Code hat dann nicht die unangenehmen Änderungen gegenüber dem Standard wie im JavaScript 1.2-Block, wird aber dennoch nur in JavaScript 1.2-Browsern ausgeführt. Diese Option wird allerdings selten gewählt, da es wirksamere Methoden gibt und sie bereits einiges an Code erfordert.
14 15 16
TIPP
17 18
Eine frühere JavaScript-Version, also JavaScript 1.1, zu verwenden, macht nur dann Sinn, wenn nur sehr alte Browser ausgeschlossen werden sollen. Davon konkret betroffen sind der Internet Explorer 3 in der ersten Variante und Netscape 2, die beide nur JavaScript 1.0 (= JScript 1.0) unterstützen. Ob diese Browser aber ausgeschlossen werden oder nicht, ist weniger von Bedeutung, da die Verbreitung gegen 0 geht.
19 20
Neuere JavaScript-Versionen wie 1.6 und 1.7 sind dagegen problematisch, da sie nur Mozilla-konforme Browser erkennen, was in der Praxis selten gewünscht ist.
21
Das Fazit fällt im Moment klar aus: JavaScript 1.2 und 1.1 sind unsinnige oder unnötige Angaben und JavaScript 1.3 schließt beinahe zu viel aus. Und auch bei moderneren JavaScript-Versionen ist die Versionsangabe nicht eindeutig. Die Browserunterscheidung mit dem language-Attribut wird also in der Praxis selten verwendet und nur dann zum Einsatz kommen, wenn Sie ganz bestimmte Codeschnipsel nur für eine bestimmte Version und damit einen engen Kreis von Browsern schreiben möchten. Sie ist aber, beispielsweise in einer Ajax- oder DHTML-Anwendung, nicht zur Browserunterscheidung geeignet.
14.2
22 23 24
Deaktiviertes JavaScript
Abschnitt 1.5.1 »Alte Browser und deaktiviertes JavaScript« verrät Ihnen, wie Sie -Bereiche hinzufügen, um Browser ohne oder mit deaktiviertem JavaScript abzufangen. Hier gilt noch eine zusätzliche Regel. Testen Sie so viel wie möglich! JavaScript können Sie bei allen hier verwendeten Browsern deaktivieren (Ausnahme: Netscape Navigator 2 bis Unterversion 2.01). 387
Browserunterscheidung
Nicht JavaScript-fähige Browser sind beispielsweise der reine Textbrowser Lynx und der NCSA Mosaic. REF
Abbildung 14.1: In einem Browser ohne JavaScript wird der Inhalt des Bereichs ausgegeben.
Außer dem -Bereich gibt es keine Möglichkeit, deaktiviertes JavaScript oder nicht JavaScript-fähige Browser festzustellen. Das wird verständlich, wenn Sie bedenken, dass diese Browser entweder keinen JavaScript-Interpreter haben oder dieser abgeschaltet ist. Wie sollten Sie da per Skript feststellen können, ob JavaScript funktioniert?
14.3
navigator-Objekt
Das navigator-Objekt liefert eine beträchtliche Menge an Informationen über den Browser.5 Darunter fällt die Angabe der Browserversion, des Browsernamens und der Sprache des lokalen Systems.
INFO
Der Internet Explorer ab Version 4 besitzt das Objekt clientInformation. Es enthält dieselben Eigenschaften und Methoden wie das navigator-Objekt, das der Internet Explorer ebenfalls besitzt. In den meisten Fällen verwenden Sie vermutlich navigator, da dies für alle Browser gilt. Benötigen Sie dagegen nur die Informationen vom Internet Explorer, können Sie auch clientInformation einsetzen.6
14.3.1 Welcher Browser? Das navigator-Objekt besitzt drei Eigenschaften, aus denen im Zusammenspiel der Browser und seine Version ausgelesen werden können: 5 6
388
Navigator ist der englische Begriff für Browser. Andere Browser melden bei diesem Objekt natürlich, dass sie es nicht unterstützen.
navigator-Objekt
■ ■ ■
appName – der allgemein gültige Name des Browsers appVersion – die Browserversion userAgent – enthält Browser und Browserversion in detaillierterer Form
Diese drei Eigenschaften müssen zusammen betrachtet werden, weil sich die Angaben – wen wundert es? – je nach Browser deutlich unterscheiden und nur mit allen dreien eine gute Filterwirkung gewährleistet ist.
14
Alle drei Eigenschaften sind auch in Uraltbrowsern wie Netscape 2 und Internet Explorer 3 ab der ersten Version implementiert. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
appName
appVersion
userAgent
15 Tabelle 14.1: Die Eigenschaften zur Browsererkennung
17
Feststellen
18
Der erste Schritt ist einfach. Die drei Eigenschaften werden für den Browser ausgelesen: Listing 14.1:
19
Die Browserversion ausgeben (navigator.html)
Browser
Der Internet Explorer gibt auch eine Rückmeldung darüber, ob das .NET Framework installiert ist (.NET). Zusätzlich gibt er die Versionsnummer der Common Language Runtime (CLR) aus. Sind zwei Versionen installiert, ist auch das zu sehen.
16
20 21 22 23 24 INFO
389
Browserunterscheidung
Abbildung 14.2: Der Internet Explorer 7 gibt an, wie er heißt, und liefert gleich mit, dass das .NET Framework installiert ist.
Tabelle 14.2 enthält eine Sammlung der Ausgaben von den wichtigsten Browsern. Diese Sammlung ließe sich mit älteren Browserversionen und verschiedensten Betriebssystemen beliebig erweitern. Das Ziel ist allerdings zuerst einmal nicht, dass Sie die Tabelle auswendig können, sondern aus den Ergebnissen die Erkenntnisse für die Browserunterscheidung ziehen. Tabelle 14.2: Die Ausgaben der wichtigsten Browser
Browser
Wert
IE 4
appNameMicrosoft Internet Explorer appVersion4.0 (compatible; MSIE 4.01; Windows NT) userAgentMozilla/4.0 (compatible; MSIE 4.01; Windows NT)
IE 5
appNameMicrosoft Internet Explorer appVersion4.0 (compatible; MSIE 5.01; Windows NT 5.0; .NET CLR 1.0.3705) userAgentMozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; .NET CLR 1.0.3705)
IE 5.5
appNameMicrosoft Internet Explorer appVersion4.0 (compatible; MSIE 5.5; Windows NT 5.0; .NET CLR 1.0.3705) userAgentMozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; .NET CLR 1.0.3705)
IE 6
appNameMicrosoft Internet Explorer appVersion4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705) userAgentMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)
IE 7
appNameMicrosoft Internet Explorer appVersion4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) userAgentMozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
390
navigator-Objekt
Browser
Wert
NN 4.x
appNameNetscape appVersion4.78 [de] (Windows NT 5.0; U) userAgentMozilla/4.78 [de] (Windows NT 5.0; U)
NN 6.2
appNameNetscape appVersion5.0 (Windows; de-DE) userAgentMozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:0.9.4.1) Gecko/20020508
Tabelle 14.2: Die Ausgaben der wichtigsten Browser (Forts.)
14
Netscape6/6.2.3
15
appNameNetscape appVersion5.0 (Windows; de-DE) userAgentMozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.0.2) Gecko/20021120 Net-
NN 7
scape/7.01
16
appNameNetscape appVersion5.0 (Windows; en-US) userAgentMozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2.1) Gecko/20021130
17
FF2
appNameNetscape appVersion5.0 (Windows; de) userAgentMozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1) Gecko/20061010 Firefox/2.0
18
Op 9.1
appNameOpera appVersion9.10 (Windows NT 5.1; U; de) userAgentOpera/9.10 (Windows NT 5.1; U; de)
Ko 3
appNameKonqueror appVersion5.0 (compatible; Konqueror/3; Linux) userAgentMozilla/5.0 (compatible; Konqueror/3; Linux)
Mo
19 20 21
Opera
22
Der Opera hat vor längerer Zeit eine Funktion eingeführt, die für den Nutzer praktisch, für den Entwickler dagegen problematisch ist. Opera kann sich als beliebiger anderer Browser identifizieren. In älteren Versionen war das Standardverhalten eine Identifikation als Internet Explorer, mittlerweile identifiziert er sich standardmäßig als Opera. Das Ziel hinter der Verschleierung war offensichtlich: Viele Browserunterscheidungen trennen nur Netscape Navigator und Internet Explorer, Opera wurde ignoriert. Damit OperaSurfer aber nicht auf eine Seite ohne JavaScript geschickt werden, gibt sich Opera lieber als anderer Browser aus.
23 24
Dies wirft zwei Probleme auf: ■
Zum einen sind Browserstatistiken im Web für Opera nach wie vor nicht 100-prozentig glaubwürdig, da einige Opera-Installationen sich nicht als Opera identifizieren.
391
Browserunterscheidung
■
Noch gravierender ist allerdings, dass es in einer Browserunterscheidung kaum möglich ist, den Opera in älteren Versionen von anderen Browsern zu unterscheiden.
Die einzige Unterscheidungsmöglichkeit liegt in der Zeichenkette Opera, die unter userAgent vorhanden ist. Konqueror Der Konqueror identifiziert sich zwar normal als Konqueror, Sie können allerdings unter EINSTELLUNGEN/KONQUEROR EINRICHTEN zusätzliche Angaben machen, wie er sich genau ausgeben soll. Abbildung 14.3: Die Einstellungen für den Konqueror
INFO
USER-AGENT-KENNUNG SENDEN zu deaktivieren hat für JavaScript keine Auswirkung, sondern bezeichnet die Versendung der User-Agent-Identifizierung an den Webserver per HTTP. Unterscheiden Browser exakt voneinander zu unterscheiden ist sehr schwierig bis unmöglich. Zu viele Plattformen, Browserversionen und Besonderheiten sind zu beachten.
392
navigator-Objekt
Die einfachste Unterscheidung wäre nur mit dem Namen der Browser: Listing 14.2:
Unterscheidung mit appName (name.html)
Browser
18 19
Dieses Listing erkennt Firefox und Mozilla beide als Netscape. Der Internet Explorer wird sehr sicher erkannt. Opera fällt raus oder landet je nach Identifikation beim Internet Explorer oder Netscape. Übrig bleiben außerdem Konqueror und Safari. Der Safari identifiziert sich standardmäßig als Netscape.
20
Ein weiteres Problem dieser ersten Unterscheidung: Die Versionsnummer des Browsers wird nicht festgelegt. Mit der Versionsnummer wird es gleich ein gutes Stück schwieriger. Sie lässt sich beispielsweise aus der Eigenschaft userAgent ziehen. Da dort allerdings auch Informationen zum Betriebssystem stehen und das Ganze bei den meisten Browsern mit Mozilla beginnt7, fällt das Filtern schwer. Einfacher erscheint da der Zugriff über appVersion, allerdings gibt beispielsweise Netscape ab Version 6 hier immer 5 als Versionsnummer an. Und auch die Versionsnummern von Konqueror und Opera sind versteckter.
21 22 23
Das folgende Skript ist ein Versuch, dies alles zu berücksichtigen:8 Listing 14.3:
24
Eine umfangreiche Browserunterscheidung mit Versionsnummer (unterscheidung.html)
Browser = 5)) { ausgabe = "MO"; } else if (name == "Netscape") { ausgabe = "NN"; } else if (name == "Microsoft Internet Explorer") { ausgabe = "IE"; } else { ausgabe= "NB"; } var erg = eval("ver" + ausgabe + "()"); return ausgabe + " " + erg; } function verOP() { if (name == "Opera") { return parseFloat(vers); } else if (name == "Netscape") { if (parseFloat(vers) < 5) { return "Opera als NN " + parseFloat(vers); } else { return "Opera als NN 6+"; } } else { return "Opera als IE " + verIE(); } } function verKO() { var pos = ua.indexOf("Konqueror/"); var string = ua.substring(pos + 10, pos + 14); return parseFloat(string); } function verMO() { var pos = ua.indexOf("rv:"); var string = ua.substring(pos + 3, pos + 7); return parseFloat(string); } function verNN() { if (parseFloat(vers) < 5) { return parseFloat(vers) } else if (ua.indexOf("Netscape/") != -1) { var pos = ua.indexOf("Netscape/"); var string = ua.substring(pos + 9, pos + 13); return parseFloat(string); } else {
394
navigator-Objekt
var pos = ua.indexOf("Netscape"); var string = ua.substring(pos + 8, pos + 12); return parseFloat(string); } } function verIE() { var pos = ua.indexOf("MSIE"); var string = ua.substring(pos + 5, pos + 9); return parseFloat(string); } function verNB() { return parseFloat(vers); }
14 15 16
document.write(browser()) //-->
17 18
Das Skript besteht aus zwei Teilen: der Funktion browser(), die den Browsernamen identifiziert vielen Funktionen, die für die einzelnen Browser die Versionsnummer extrahieren und ihr Ergebnis an browser() zurückliefern
19
Am einfachsten lässt sich der Ablauf der Browserunterscheidung so darstellen:
20
■ ■
1.
Zuerst wird in einer Fallunterscheidung in der Funktion browser() geprüft, um welchen Browser es sich handelt. Als Beispiel folgt eine Zeile für den Internet Explorer:
21
else if (name == "Microsoft Internet Explorer") {
2.
22
Die Variable ausgabe nimmt das Ergebnis, in diesem Fall das Kürzel des Browsers, auf. ausgabe = "IE";
23
}
3. Aus der Variablen, also dem Browserkürzel, und dem String ver wird per eval()-Anweisung ein Funktionsaufruf gemacht, der genau die Funktion für den erkannten Browser aufruft. Den Rückgabewert der Funktion zum Versionscheck speichert die Variable erg.
24
var erg = eval("ver" + ausgabe + "()");
4. Die Funktionen zum Versionscheck unterscheiden sich von Browser zu Browser deutlich. Beim Internet Explorer muss die Versionsnummer aus der Eigenschaft userAgent gezogen werden, da appVersion ab der fünften Generation immer noch 4 ist. In userAgent folgt die Versionsnummer dagegen immer nach der Zeichenkette "MSIE", auf deren Posi-
395
Browserunterscheidung
tion Sie mit der String-Methode indexOf("String") zugreifen können. Die Methode substring(Start, Ende) schneidet dann die Versionsnummer heraus und parseFloat("String") wandelt sie in eine Zahl um: function verIE() { var pos = ua.indexOf("MSIE"); var string = ua.substring(pos + 5, pos + 9); return parseFloat(string); }
HALT
Sie sollten statt substring() nicht slice() verwenden, da diese Methode in älteren Browsern wie Netscape 2 und 3 und Internet Explorer 3 nicht vorhanden ist. Eine Übersicht über die String-Methoden finden Sie in Kapitel 7 »Arrays und Strings«. 5. Die Funktion zum Versionscheck liefert dann an die Variable erg in der Funktion browser() die Versionsnummer zurück. Dort werden dann Browserkürzel und Funktionsnummer gemeinsam zurückgeliefert. return ausgabe + " " + erg;
Bei einigen Fallunterscheidungen und Versionsüberprüfungen lohnt sich eine genauere Betrachtung. Hierzu finden Sie in den nächsten Abschnitten Details. Opera Der Opera ist bekanntlich flexibel und kann sich als beliebiger Browser identifizieren. Daher hilft appName auch nicht weiter. Allerdings gibt es, unabhängig davon, als welcher Browser sich Opera identifiziert, immer die Zeichenkette »Opera« in userAgent. Daher erfolgt die Identifikation auch über diese Zeichenkette. Die Methode indexOf("String")gibt an, ob die Zeichenkette in userAgent vorhanden9 ist. if (ua.indexOf("Opera") != -1) { ausgabe = "OP"; }
INFO
Beachten Sie, dass Opera vor den anderen Browsern geprüft werden muss. Wenn sich Opera als Netscape oder Internet Explorer identifiziert, wird die Fallunterscheidung bereits beendet, bevor sie zu Opera gelangt. Mozilla Mozilla ist ein schwer zu fassender Geselle. Er heißt Netscape (appName); dies ist aber nicht ausreichend. Er darf auch im userAgent nicht die Zeichenkette »Netscape« haben. Dies können Sie mit indexOf() prüfen. Solche Zei-
9
396
Noch exakter prüft sie, ob sie nicht vorhanden ist. Das heißt, ob indexOf() nicht –1 ergibt. –1 würde bedeuten, dass die Zeichenkette nicht vorhanden ist.
navigator-Objekt
chenkette besitzen allerdings Netscapes vor Version 6, also 2, 3, 4.x, auch nicht. Um diese Browser auszuschließen, muss die Versionsnummer (appVersion) größer als 5 sein. else if ((name == "Netscape") && (ua.indexOf("Netscape") == -1) && (parseFloat(vers) >= 5)) { ausgabe = "MO"; }
Sie können sich in der Praxis durchaus auch dafür entscheiden, nicht auf Mozilla zu prüfen, sondern ihn in eine Schublade mit Netscape zu stecken. Sie müssen dann lediglich die Versionsnummernüberprüfung anpassen.
14 15 TIPP
Version von Opera
16
Die Version von Opera benötigt eine Fallunterscheidung, ob Opera sich als Opera, Netscape oder Internet Explorer ausgibt. Identifiziert er sich als Opera, ist es ausreichend, die Versionsnummer aus appVersion auszulesen.
17
Als Netscape gibt er dagegen bei einer höheren Version in appVersion an, er sei Netscape 5. Dagegen hilft eine Fallunterscheidung für Netscape mit einer Version niedriger oder größer gleich 5.
18
Für den Internet Explorer wird die Versionsnummer aus der Funktion zur Versionserkennung des Internet Explorers (verIE()) gewonnen.
19
function verOP() { if (name == "Opera") { return parseFloat(vers); } else if (name == "Netscape") { if (parseFloat(vers) < 5) { return "Opera als NN " + parseFloat(vers); } else { return "Opera als NN 6+"; } } else { return "Opera als IE " + verIE(); } }
20 21 22 23
Version von Mozilla
24
Die Version des Mozilla steht hinter der Zeichenkette »rv:«. Der Zugriff darauf erfolgt wie auf die Versionsnummer des Internet Explorers (siehe Seite 389). function verMO() { var pos = ua.indexOf("rv:"); var string = ua.substring(pos + 3, pos + 7); return parseFloat(string); }
397
Browserunterscheidung
Version von Netscape Der Netscape Navigator erfordert eine dreiteilige Fallunterscheidung: ■
Zuerst werden die Versionen kleiner als 5, also 2, 3, 4.x, herausgefiltert und deren Versionsnummer aus appVersion direkt mit parseFloat() ausgelesen. function verNN() { if (parseFloat(vers) < 5) { return parseFloat(vers) }
■
Anschließend muss zwischen verschiedenen Schreibweisen unterschieden werden. Netscape 7 schreibt die Versionsnummer in userAgent immer nach dem String »Netscape/«, Netscape 6 dagegen nach »Netscape« ohne Schrägstrich. else if (ua.indexOf("Netscape/") != -1) { var pos = ua.indexOf("Netscape/"); var string = ua.substring(pos + 9, pos + 13); return parseFloat(string); } else { var pos = ua.indexOf("Netscape"); var string = ua.substring(pos + 8, pos + 12); return parseFloat(string); } }
Verbesserungen Nichts ist perfekt, leider auch nicht diese Fallunterscheidung. Entsprechend könnten Sie noch beliebige Verbesserungen vornehmen. Eine wäre, die Angaben der drei Eigenschaften des navigator-Objekts in Kleinbuchstaben oder Großbuchstaben zu verwandeln und nur diese zu vergleichen. Dadurch umgehen Sie Verwechslungen bei der Groß- und Kleinschreibung, die eventuell in manchen Versionen auftauchen könnten. Diese Variante finden Sie unter dem Namen unterscheidung_gk.html im Ordner code\kap14 im Download-Archiv. WWW
Eine weitere Verbesserungsmöglichkeit wäre, noch mehr Fehlerüberprüfungen einzubauen. Beispielsweise ließe sich jede parseFloat()-Anweisung für die Versionsnummer mit einer Fallunterscheidung versehen, die mit isNaN() überprüft, ob auch tatsächlich eine Zahl zurückgeliefert wird. Weiterleitung Eine Browserunterscheidung ist insbesondere dann sinnvoll, wenn Sie für verschiedene Browser auch unterschiedliche Seiten vorbereitet haben. Interessant ist dies in der Praxis vor allem, um ältere Browser auf eine eigene »Low-Level«-Seite umzuleiten.
398
navigator-Objekt
Als Basis für diese Beispielanwendung dient das Browserunterscheidungsskript aus dem letzten Abschnitt (siehe Listing 14.3). Verändert wird Folgendes: ■
■
Bei der Überprüfung des Browsers kommt eine Fallunterscheidung hinzu. Sie bezieht die Versionsnummer aus den Funktionen zum Versionsnummerncheck, die Ihnen ja aus dem bereits gesehenen Beispiel bekannt sind. Sie können hier für jeden Browser flexibel die Versionsnummer wählen, ab der er auf die Seite für neue Browser umgeleitet wird. Ist die Versionsnummer groß genug, bei Netscape beispielsweise größer gleich 4, erhält die Variable ausgabe den Wert true.
Die Wahl der Versionsnummern müssen Sie nicht aus dem Beispiel übernehmen, sondern Sie können beliebige eigene Einschränkungen machen. Im Allgemeinen gilt: je weniger Einschränkungen, desto besser. Die Browser, die Sie auf die anspruchsvollere Seite weiterleiten, müssen das JavaScript dort allerdings wirklich packen (testen!). ■
14 15 16 INFO
17
Nach den Überprüfungen des Browsers prüft eine eigene Fallunterscheidung den Wert von ausgabe. Ist er true, handelt es sich um einen neuen Browser und das Skript leitet auf die Seite für neue Browser weiter. Ist er false, handelt es sich um einen alten Browser, und es wird auf alte_browser.html weitergeleitet.
18 19
Nachfolgend sehen Sie diesen Teil des Skripts: var var var var
20
name = navigator.appName; vers = navigator.appVersion; ua = navigator.userAgent; ausgabe = false;
21
function browser() { if (ua.indexOf("Opera") != -1) { if (verOP() >= 6) { ausgabe = true; } } else if (name == "Konqueror") { if (verKO() >= 3) { ausgabe = true; } } else if ((name == "Netscape") && (ua.indexOf("Netscape") == -1) && (parseFloat(vers) >= 5)) { ausgabe = true; } else if (name == "Netscape") { if (verNN() >= 4) { ausgabe = true; } } else if (name == "Microsoft Internet Explorer") { if (verIE() >= 4) { ausgabe = true;
22 23 24
399
Browserunterscheidung
} } else { ausgabe= false; } if (ausgabe) { window.location.href = "neue_browser.html"; } else { window.location.href = "alte_browser.html"; } }
In den Funktionen zur Versionsüberprüfung ändert sich nur bei Opera etwas. Opera könnte sich ja theoretisch als Netscape 3 (Mozilla 3.0) ausgeben. Dann würde er vom Skript zu den alten Browsern gesteckt, was bei Opera 6 oder 7 keinen Sinn macht. Daher liest die Funktion zur Versionsüberprüfung (verOP()) die Versionsnummer aus userAgent, wo sie nach der Zeichenkette "Opera" und einem Leerzeichen folgt. function verOP() { var pos = ua.indexOf("Opera"); var string = ua.substring(pos + 6, pos + 9); return parseFloat(string); }
INFO
Natürlich ist die Frage, was Opera-Nutzer erreichen möchten, die sich als Mozilla 3 oder 4.78 ausgeben. Es werden die wenigsten sein – so viel sei verraten. Ihre wahren JavaScript-Fähigkeiten zu erkennen macht Sinn, denn wenn sie JavaScript nicht wollten, könnten sie es auch deaktivieren.
Abbildung 14.4: Opera 7 wird als neuer Browser erkannt, obwohl er sich als Mozilla 3.0 ausgibt.
Den vollständigen Code finden Sie im Download-Archiv im Ordner code\ kap14 mit dem Namen umleitung.html. WWW
400
navigator-Objekt
Abbildung 14.5: Netscape 2 dagegen landet bei den alten Browsern.
14 15 16 Verbesserungen
17
Verbesserungen sind natürlich immer möglich. Hier gilt wieder, dass zusätzliche Fehlerüberprüfungen eingebaut werden könnten. Des Weiteren sollten Sie Ihre Browserumleitung auf jedem nur verfügbaren Browser und Betriebssystem testen.
18
Wenn neue Browser am Markt erscheinen, sollten Sie auf jeden Fall überprüfen, ob der Test noch funktioniert. Bei Versionsüberprüfungen sollten Sie als Angabe natürlich immer größer als statt gleich verwenden, da anderenfalls spätestens in der nächsten Version, vielleicht aber nur beim MiniUpdate von 7 auf 7.01, die Umleitung nicht mehr funktioniert.
19 20
Eine weitere sinnvolle Verbesserung in diesem Skript ist natürlich ein -Container, der nicht JavaScript-fähige Browser oder Browser mit deaktiviertem JavaScript abfängt.10
21
Auf der Seite alte_browser.html sollte ein Link der Art »Ihr Browser kann mehr, hier geht’s zur Superseite« integriert sein, falls die Versionsüberprüfung wirklich mal versagt oder Sie einen neuen Browser nicht schnell genug integriert haben.
22 23
14.3.2 Browser-Sniffer
24
Das Thema Browsererkennung wird in der Webentwickler-Community heiß diskutiert. Vertreter neuerer Ansichten gehen häufig davon aus, dass Browserunterscheidung an sich nicht sinnvoll ist und man besser browserübergreifend programmieren sollte; andere sind Sniffer-Fans. Im Web finden sich hunderte von Sniffer-Skripten. Einen interessanten Ansatz verfolgt der Sniffer-Generator unter http://www.guppi.de/sniffergenerator.shtml. Er erlaubt die Angabe von vier Browsern, die jeweils gefiltert werden sollen. 10 Eigentlich ist das ja ohnehin obligatorisch, oder?
401
Browserunterscheidung
Einer der bekanntesten Sniffer ist der Ultimate JavaScript Client Sniffer von Erik Krock, einem Netscape-Mitarbeiter und Mozilla-Entwickler (http:// www.mozilla.org/docs/web-developer/sniffer/browser_type.html). Er ist nicht mehr aktuell und wird vom Mozilla-Projekt nur noch aus historischen Gründen angeboten, bildet aber den Vater vieler anderer Sniffer, die durchs Netz geistern. Prinzipiell spricht nichts gegen den Einsatz dieser Sniffer, Sie sollten sie jedoch insbesondere mit aktuellen Browsern testen. Wenn dann neue Browser hinzukommen, benötigen Sie häufig auch eine neue SnifferVersion. Stehen die Änderungen dann nicht sofort im Netz zur Verfügung, ist dies ärgerlich, weil viele Nutzer am Anfang neue Browser ausprobieren. Abgesehen davon ist es nicht ganz einfach, Sniffer an die eigenen Bedürfnisse anzupassen. Sie liefern häufig zu viele Informationen. Als Fazit bleibt: Wer gerne auf die Rundumversorgung setzt, erreicht sein Ziel mit einem Sniffer schneller, wer dagegen flexibler sein und mit weniger Code auskommen möchte, sollte die Lösung selbst programmieren. Ob ein Sniffer empfehlenswert ist, ist natürlich davon abhängig, was Sie damit erreichen möchten: Die meisten Sniffer werden heute für statistische Zwecke verwendet und sollen feststellen, um welche Browser es sich handelt. Ein optionaler JavaScript-Teil unterstützt dabei nur die serverseitige Technologie, die das HTTP-Feld mit den Browserinformationen ausliest. Die klassische Browserunterscheidung mit Sniffer hat dagegen eher ausgedient.
14.3.3 Sprache feststellen Die Sprache lässt sich relativ einfach feststellen. Sie besteht aus einem Kürzel aus zwei Buchstaben: de steht für Deutsch, en für Englisch, it beispielsweise für Italienisch. Allerdings gibt es hier wieder einen kleinen, aber ärgerlichen Unterschied bei den Browsern. Die Eigenschaft für das navigator-Objekt, um die Sprache (des Browsers) auszulesen, heißt im Internet Explorer browserLanguage, bei Netscape dagegen nur language. Lediglich Opera versteht beides. Der Safari identifiziert sich in Version 1.0 immer mit Englisch als Sprache. Erst in späteren Versionen ist dieser Bug beseitigt. Tabelle 14.3: Eigenschaften zur Spracherkennung
NS4.x
M/FF
browserLanguage language
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Um dieses Problem zu umgehen, hilft bei der Wahl der Sprache eine Fallunterscheidung:
402
navigator-Objekt
Listing 14.4:
Die Sprache feststellen (sprache.html)
Sprache
14 15 16 17
Die Fallunterscheidung nutzt den Umstand, dass die Bedingung true ergibt, wenn eine Eigenschaft vorhanden ist, false dagegen, wenn der Browser die Eigenschaft nicht unterstützt. Der Netscape Navigator unterstützt navigator.browserLanguage beispielsweise nicht, liefert also false. Dagegen kennt er navigator.language aus der else-if-Bedingung und gibt true zurück.
18 19
Weiterleitung
20
Eine einfache Weiterleitung ist schnell implementiert. Sie liest die Sprache aus und prüft mit indexOf(), ob de oder en vorkommt. Je nachdem wird der Nutzer dann entweder auf die deutsche oder englische Seite weitergeleitet. Hat der Browser eine andere Sprache, bleibt der Nutzer auf der Weiterleitungsseite und sieht die im HTML-Code angelegten Links auf die zwei vorhandenen Sprachversionen. Er kann sich dann selbst entscheiden. Listing 14.5:
21 22
Je nach Sprache weiterleiten (sprache_umleitung.html)
Sprache
23 24
403
Browserunterscheidung
English (Englisch) Deutsch (German)
Abbildung 14.6: Die Weiterleitung hat funktioniert und der Nutzer ist auf der deutschen Seite gelandet.
Die Funktion indexOf() ist einem Vergleich mit == vorzuziehen, da bei einigen Länderkürzeln und Browsern noch Nachsätze folgen. So steht de-at zum Beispiel im Internet Explorer für Österreich, der Navigator gibt Deutschland als de-DE an. Im englischen Sprachraum wird zwischen en-GB und en-US (Netscape) bzw. en-gb und en-us zwischen Großbritannien (gb) und den USA (us) unterschieden. Auch wenn Sie die Sprache automatisch wählen, sollten Sie dem Nutzer immer die Möglichkeit geben, die Sprache wechseln zu können. Vielleicht hat er beispielsweise nur einen englischen Browser, weil er die neueste Version ausprobieren möchte, ist aber deutschsprachig.
INFO
In der Praxis wird eine Sprachunterscheidung auch häufig serverseitig realisiert (siehe Abschnitt 14.5 »Serverseitige Browserunterscheidung«), da je nach Sprachversion unterschiedliche Daten aus der Datenbank geholt werden. Dies funktioniert auch bei deaktiviertem JavaScript. Internet Explorer spezial Die Grundlage der Sprachunterscheidung ist nicht das Betriebssystem, sondern die Sprachversion des Browsers. Dies kann unter Umständen unpräzise sein, wenn der Nutzer nur einen englischen Browser hat, aber Deutsch spricht. Microsoft hat dafür zwei Eigenschaften namens systemLanguage und userLanguage eingeführt, die auf die Sprachversion des Betriebssystems bzw. der Benutzereinstellungen zurückgreifen.
Tabelle 14.4: Internet ExplorerEigenschaften
404
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
systemLanguage
userLanguage
Op
SF/KQ
navigator-Objekt
Die zwei Eigenschaften werden genauso wie browserLanguage eingesetzt: Listing 14.6: Die Eigenschaften des Internet Explorers für die Systemsprache (Ausschnitt aus: sprache_ie.html) if (navigator.systemLanguage) { document.write(navigator.systemLanguage); document.write(""); document.write(navigator.userLanguage); }
Die Systemsprache kann in manchen Betriebssystemen geändert werden. In Windows XP finden Sie dies im Menü SYSTEMSTEUERUNG/REGIONS- UND SPRACHOPTIONEN. Auf der Registerkarte REGIONALE EINSTELLUNGEN ändern Sie die Einstellungen per Auswahlliste. Diese Wahl beeinflusst die Eigenschaft userLanguage (nicht systemLanguage).11
14 15 INFO
16 Abbildung 14.7: userLanguage gibt gl für Galizisch an – da kann doch etwas nicht stimmen, oder?
17 18 19 20 21
Ältere Browser
22
Für ältere Browser, die die Eigenschaften language oder browserLanguage nicht verstehen, vor allem Netscape 2 und 3 und Internet Explorer 4, können Sie die Angabe auch aus den Eigenschaften appVersion oder userAgent des navigator-Objekts ziehen.
23
Dazu benötigen Sie Folgendes: ■
24
Eine Variable mit dem Wert der Eigenschaft userAgent var ua = navigator.userAgent;
■
Eine Fallunterscheidung zwischen den Sprachen, die Sie unterstützen: if (navigator.userAgent.indexOf("de") != -1) { sprache = "de"; } else if (navigator.userAgent.indexOf("en") != -1) { sprache = "en"; }
11 Natürlich auch nicht browserLanguage, da dies nur vom Browser abhängig ist.
405
Browserunterscheidung
Diesen Code können Sie nun in Listing 14.5 einfügen. Die Fallunterscheidung kommt in die else-Anweisung der dortigen Fallunterscheidung: Listing 14.7:
Weiterleitung nach Sprache für ältere Browser (sprache_alt.html)
Sprache
English (Englisch) Deutsch (German)
Abbildung 14.8: Auch in Netscape 2 funktioniert die Weiterleitung.
406
navigator-Objekt
Sprachversionen Die folgende Tabelle fasst die wichtigsten Sprachkürzel zusammen. Kürzel
Sprache
Kürzel
Sprache
ar
Arabisch
fi
Finnisch
bg
Bulgarisch
fr
Französisch
cs
Tschechisch
hu
Ungarisch
da
Dänisch
it
Italienisch
de
Deutsch
ja
Japanisch
de-at
Deutsch (Österreich)
ko
Koreanisch
de-ch
Deutsch (Schweiz)
nl
Niederländisch
de-li
Deutsch (Liechtenstein)
no
Norwegisch
de-lu
Deutsch (Luxemburg)
pl
Polnisch
el
Griechisch
pt
Portugiesisch
en
Englisch
ru
Russisch
en-au
Englisch (Australien)
sk
Slowakisch
en-ca
Englisch (Kanada)
sr
Serbisch
en-gb
Englisch (Großbritannien)
sv
Schwedisch
en-us
Englisch (USA)
tr
Türkisch
es
Spanisch
zh
Chinesisch
Tabelle 14.5: Einige wichtige Sprachkürzel
14 15 16 17 18 19 20 21
Um ein spezielles Sprachkürzel zu erhalten, können Sie dies auch mit dem Internet Explorer (userLanguage) und einem Betriebssystem testen, das Sprachwechsel erlaubt: Die Sprachkürzel der Browser gleichen im Übrigen nicht vollständig den entsprechenden Toplevels. en-gb steht beispielsweise für Großbritannien, während der Toplevel co.uk ist.
22 23 INFO
24
14.3.4 Plugins Das navigator-Objekt besitzt Eigenschaften (beispielsweise plugins), um festzustellen, welche Plugins auf dem Rechner des Nutzers installiert sind. Fehlt beispielsweise das Flash-Plugin oder der Windows Media Player, kann JavaScript automatisch nachfragen.
407
Browserunterscheidung
Mehr Informationen zur Verwendung erhalten Sie in Kapitel 24 »Multimedia, Java etc.«. REF
14.3.5 Sinnvoll? Ist die Browserüberprüfung mit dem navigator-Objekt wirklich sinnvoll? Hier gibt es nur eine Antwort: Das kommt darauf an. Heute gilt eine zweite Version einer Website in den meisten Fällen als zu viel Arbeit. Der Marktanteil der besonders problematischen Browser, Netscape Navigator 4 und Internet Explorer 4, ist einfach zu gering und viele Nutzer sind dank moderner Web 2.0-Effekte durchaus motiviert, einen neueren Browser zu verwenden. Das Browser-Sniffing kommt deswegen in der Praxis eher für statistische Zwecke als für separate Website-Versionen zum Einsatz. Sie müssen die Browserunterscheidung mit jedem neuen Browser testen. Sonst macht sie keinen Sinn. HALT
Haben Sie nur auf bestimmten Seiten ein Ajax oder DHTML, ist eine Browserüberprüfung mit den Browserobjekten besser geeignet, da die Browserunterscheidung kaum so genau werden kann und nie exakt feststellt, welches Objekt von einem Browser unterstützt wird. Das automatische Auslesen der Sprachversionen wird im Web aktuell verwendet. Es handelt sich prinzipiell um eine gute Technik, die in Verbindung mit einem -Bereich für nicht JavaScript-fähige Browser ausreichend kompatibel ist, wenn der Nutzer jederzeit manuell die Sprachen wechseln kann. Allerdings setzen größere Websites und Content-Managment-Systeme eher auf die Spracherkennung per serverseitige Technologie.
14.4 Browserobjekte Die Browserunterscheidung mit den Browserobjekten stellt nicht fest, um welchen Browser es sich handelt, sondern nur, ob der Browser ein bestimmtes Objekt verwendet. Ein ähnliches Beispiel haben Sie bereits in Abschnitt 14.3.3 »Sprache feststellen« gesehen. Auch in den Ajax-Kapiteln im vorangegangenen Buchteil sind diese Arten von Überprüfung schon zum Einsatz gekommen. Listing 14.8: Ist das Browserobjekt vorhanden? (unterscheidung_browserobjekte.html) if (document.getElementById) { document.write("getElementById vorhanden!"); }
Die if-Bedingung aus Listing 14.8 ergibt nur true, wenn der Browser die Methode getElementById() unterstützt.
408
Browserobjekte
Abbildung 14.9: Browser und Nutzer freuen sich: getElementById wird unterstützt.
14 15 Diese Unterscheidung wird natürlich nicht dazu verwendet, freudig auszugeben, dass die Methode unterstützt wird. Aber in dieser Fallunterscheidung kann getElementById ab jetzt gefahrlos verwendet werden. Ältere Browser kennen getElementById nicht, geben der if-Bedingung false zurück und ignorieren die weiteren Anweisungen.
16 17
14.4.1 Ajax/DHTML und Browserunterscheidung
18
In der Praxis wird der Test der Browserobjekte meist in Ajax- und DHTMLAnwendungen eingesetzt.12 Ein einfaches Beispiel soll das hier zeigen. In den folgenden Kapiteln wird die Browserunterscheidung mit Objekten noch häufig verwendet.
19
Das Beispiel blendet einen Text ein, wenn der Nutzer über einen Link fährt (onmouseover), und blendet ihn – in derselben Funktion – wieder aus, wenn er den Link verlässt (onmouseout). Die Browserunterscheidung wird notwendig, wenn auf den leeren Absatz (-Tag) zugegriffen werden soll: ■
getElementById()
■
21
Der W3C-konforme Zugriff per getElementById() funktioniert nur in neueren Browsern. NS4.x M/FF
■
20
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 14.6: getElementById()
22 23
Der Internet Explorer 4 und ältere Opera-Versionen benötigen die Kollektion document.all, die alle Elemente der Seite enthält. Neuere Internet Explorer- und Opera-Versionen unterstützen sie immer noch. Der Netscape Navigator 4.x verwendet als Einziger document.layers. Zudem ist für den Zugriff auf eine Stil-Eigenschaft wie visibility (Sichtbarkeit) kein style-Objekt vorhanden.
24
12 Wenn man den DHTML-Begriff weiter fasst und jede Art von Interaktion mit einbezieht, verwenden fast ausschließlich DHTML-Anwendungen die Unterscheidung mit Browserobjekten (auch das Sprachbeispiel wäre dann DHTML).
409
Browserunterscheidung
Daher bietet es sich an, nach diesen verschiedenen Objekten zu unterscheiden. Eine Variable nimmt bereits die Referenz auf das Absatzobjekt auf. Hier die drei Fälle: if (document.getElementById) { absatz = document.getElementById("text1").style; } else if (document.all) { absatz = document.all("text1").style; } else if (document.layers) { absatz = document.text1; }
Der Rest ist nicht mehr schwierig. Eine weitere Fallunterscheidung fragt ab, ob der Absatz gerade sichtbar ist oder nicht. Je nachdem wird er aus- oder eingeblendet. if (absatz.visibility == "visible" || absatz.visibility == "show") { absatz.visibility = "hidden"; } else { absatz.visibility = "visible"; }
Hier der komplette Code (hervorgehoben ist die Browserunterscheidung): Listing 14.9: Browserunterscheidung in einem praktischen Beispiel (dhtml.html) Einblenden mit DHTML Anzeigen Sichtbar!
410
Browserobjekte
Abbildung 14.10: Fährt der Nutzer mit der Maus über den Link, wird der Text »Sichtbar!« angezeigt.
14 15 Abbildung 14.11: Verlässt er den Link, wird der Text wieder ausgeblendet.
16 17 18 19 20
Der Netscape Navigator 4.x macht bereits in diesem einfachen Beispiel einige Schwierigkeiten: ■ ■
Im style-Attribut lässt sich ein Kasten nur unsichtbar schalten, wenn er auch per Stil-Anweisung positioniert wird. Wollen Sie testen, ob ein Objekt eingeblendet ist, muss die visibilityEigenschaft den Wert show und nicht visible haben!13
21
TIPP
22 23
14.4.2 Sinnvoll? Die Unterscheidung mit Objekten ist immer dann sehr sinnvoll, wenn Sie das Objekt anschließend verwenden möchten. Bei DHTML ist dies die bei weitem beste Methode, da eine allgemeine Browserunterscheidung hier zu kurz greift.
24
Dies erkaufen Sie sich mit zusätzlichen Fallunterscheidungen und damit mehr Unübersichtlichkeit. Am besten lässt sich dieses Problem mit einem sinnvollen Einsatz von Funktionen und Sammelvariablen – die Referenzen allgemein verfügbar zwischenspeichern – lösen. 13 Unsichtbar ist im Netscape 4.x hide und nicht hidden. Bei der Zuweisung funktionieren visible und hidden jedoch.
411
Browserunterscheidung
Von einer Browser- und Versionsunterscheidung mittels Browserobjekten ist abzuraten. Da die Zuordnung meist nicht so einfach ist wie bei document.layers und Netscape 4.x, sind die Ergebnisse sehr unpräzise und mit einer Versionsfeststellung per navigator-Objekt nicht zu vergleichen. Zum Beleg hier ein kleines Fundstück aus dem Netz (in leicht abgewandelter und vereinfachter Form): Listing 14.10: Bitte nicht verwenden! (browser_feststellen_objekte.html) Browser mit Objekten feststellen
Zum einen sind die Ergebnisse mit den Browsern – die eigentlich getestet werden sollen – schlecht. Der Internet Explorer 6 reagiert beispielsweise auf document.docType nicht. Zum anderen bleiben andere Browser wie der Internet Explorer 5.5, Opera und Konqueror völlig außen vor. Abbildung 14.12: Der Opera ist ein Firefox?
412
Serverseitige Browserunterscheidung
14.5 Serverseitige Browserunterscheidung Im HTTP-Header, den der Browser an den Webserver sendet, steht die Browserversion und die Plattform. Die gängigen serverseitigen Programmiersprachen können darauf zugreifen. Sie könnten also auch diese Informationen verwenden, um zwischen den Browserversionen zu unterscheiden.
14
Das einzige Problem ist, dass der HTTP-Header nicht übermittelt, ob JavaScript deaktiviert ist. Sie müssen also dennoch in Ihrem Code Container vorsehen. Hier hat die clientseitige Browserüberprüfung Vorteile, da bereits in der Seite mit der Browserunterscheidung der -Container integriert sein kann. Die übrigen Seiten benötigen ihn dann nicht mehr.14 Bei der Ajax- bzw. DHTML-Versions- oder Objektüberprüfung kommen Sie ohnehin an clientseitiger JavaScript-Überprüfung mit Browserobjekten nicht vorbei.
15 16 TIPP
17
Bis hier war die serverseitige Browserüberprüfung graue Theorie. Es wird also Zeit, das Ganze in der Praxis näher zu beleuchten. Für das folgende Beispiel kommt JScript .NET zum Einsatz. JScript .NET ist die JavaScriptSprachvariante von Microsoft für den Einsatz mit der .NET-Plattform. .NET für das Internet heißt dabei ASP.NET. JScript ist als ASP.NET-Programmiersprache nicht sehr weit verbreitet, bietet Ihnen allerdings die gute Gelegenheit, mit Ihrem JavaScript-Wissen serverseitig zu programmieren.
18 19 20
INFO
ASP.NET besteht hauptsächlich aus dem .NET Framework – aktuell in der Version 2.0. Dieses Framework enthält eine sehr große Klassenbibliothek. Die Klassen in dieser Bibliothek enthalten in den Eigenschaften und Methoden die komplette Funktionalität für Ihre Anwendungen.
21 22
Für unser Beispiel ist die Rückgabe des Request-Objekts interessant. Es liefert die Antworten des Clients – also des Browsers – an den Server. Dieses Objekt enthält ein Unterobjekt Browser, das die Browserinformationen mit einer eigenen Datei, die Browserinformationen enthält, abgleicht und sie in Eigenschaften speichert.15 Folgende Eigenschaften sind hier (unter anderem) von Interesse: ■ ■ ■ ■
23 24
Browser – liefert einen String mit dem Browsernamen. MajorVersion – liefert die ganzzahlige Versionsnummer des Browsers, beim Internet Explorer 5.5 also 5. MinorVersion – liefert die Versionsnummer nach dem Komma, beim Netscape Navigator 2.02 also 02. Version – gibt die komplette Versionsnummer zurück.
14 Wenn Ihre Seiten ab der ersten Seite nur mit JavaScript funktionieren sollen. 15 In der .NET-Klassenbibliothek ist das Browser-Objekt im Namespace System.Web.HttpBrowserCapabilities gespeichert.
413
Browserunterscheidung
■
INFO
EcmaScriptVersion – prüft die Versionsnummer des unterstützten ECMAScript-Standards. Etwas verwirrend ist, dass diese Version in JavaScript-Versionsnotation – 1.1, 1.2, 1.316 – ausgegeben wird. 1.4 gibt es natürlich noch nicht, da ECMAScript erst drei Versionen hat.
Um die Klassenbibliothek zu erkunden und beispielsweise alle Eigenschaften des Browser-Objekts zu sehen, verwenden Sie am besten den Class-Browser, der beispielsweise beim kostenlos erhältlichen ASP.NET-Editor Microsoft Visual Web Developer 2005 Express dabei ist (http://www.microsoft.com/ germany/msdn/vstudio/products/express/vwd/default.mspx). Das folgende Beispiel verwendet die fünf Eigenschaften und gibt die Ergebnisse in einem ASP.NET Web Control () aus: Listing 14.11: Serverseitige Browserunterscheidung (browserpruefung.aspx) function Ausgabe(o: Object, e: EventArgs) { var bro = Request.Browser.Browser; var maj = Request.Browser.MajorVersion; var min = Request.Browser.MinorVersion; var vers = Request.Browser.Version; var ecma = Request.Browser.EcmaScriptVersion; ausgabe.Text = bro + ""; ausgabe.Text += "MajorVersion: " + maj + ""; ausgabe.Text += "MinorVersion: " + min + ""; ausgabe.Text += "Version: " + vers + ""; ausgabe.Text += "ECMAScript:" + ecma; } Serverseitig Browser auslesen
TIPP
Sie können das Beispiel nur testen, wenn Sie unter Windows den Internet Information Service (in früheren Windows-Versionen Internet Information Server) installiert haben17 und das .NET Framework besitzen.
16 Die Version 1.3 entspricht also JavaScript 1.5. 17 Er wird bei Windows NT, 2000 und XP Professional auf der CD-ROM mitgeliefert. Unter anderen Windows-Betriebssystemen müssen Sie darauf verzichten.
414
Serverseitige Browserunterscheidung
Abbildung 14.13: Der Firefox meldet sich korrekt.
14 15 16 Abbildung 14.14: Opera wird auch gut erkannt.
17 18 19 20
Abbildung 14.15: Der Internet Explorer 7 traut sich nur ECMAScript v2 zu.
21 22 23 24
415
Inhalt
15 Bilder 14
15
Als Netscape 3 ein eigenes Bildobjekt besaß1, begann der Rollover-Boom im Internet. Jede bessere (heute auch jede schlechtere) Website schmückt sich mit einer Schaltfläche, die Farbe oder Form wechselt.
16
Bilder haben allerdings noch mehr Power. In Formularen können Sie Schaltflächen oder Kontrollkästchen ersetzen. Darüber hinaus lassen sich Bilder sehr einfach animieren. Dies fällt zwar in den Bereich DHTML, ist aber ohne CSS-Stil-Anweisungen und auch in sehr alten Browsern möglich.
17
In den Browsern ab Netscape 3 und Internet Explorer 4 ist das image-Objekt komplett integriert. Der Internet Explorer 3 besitzt es eigentlich nicht, weil es in der Macintosh-Version 3.01 des Internet Explorers noch mit heißer Nadel in abgespeckter Form implementiert wurde.
18
19
image
15.1
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 15.1: Das image-Objekt
20
Rollover
21
Als Rollover-Effekt bezeichnet man den Austausch von einem Bild durch ein anderes. Dieser Bildaustausch erfolgt bei den Ereignissen onmouseover (wenn der Nutzer die Grafik mit der Maus überfährt) und onmouseout (wenn der Mauszeiger die Grafik wieder verlässt). Sie benötigen für einen Rollover-Effekt zwei Bilder mit unterschiedlichen Zuständen. Die folgenden Beispiele verwenden die Dateien schalt1.gif (blaue Schaltfläche) und schalt2.gif (dieselbe Schaltfläche in Rot).
22
23 WWW
24
Der Zugriff auf das Bild erfolgt über das document-Objekt und den Namen des Bildes. Der Name muss im name-Attribut des -Tags angegeben sein. Um das Bild zu ändern, verwenden Sie die Eigenschaft src des imageObjekts. Damit weisen Sie die Bildquelle neu zu.2
Entspricht JavaScript 1.1. src steht für source (Quelle).
417
ndex
1 2
Bilder
Listing 15.1:
Rollover-Effekt im HTML-Attribut (rollover.html)
Die Zeilen aus Listing 15.1 erledigen Folgendes: ■ ■
INFO
Im onmouseover-Attribut wird direkt die Bildquelle in schalt2.gif geändert. Das onmouseout-Attribut setzt wieder auf Bild schalt1.gif zurück.
Wenn Sie den Objektaufruf direkt in die Ereignisattribute schreiben, müssen die Anführungszeichen innerhalb der JavaScript-Anweisung andere sein als beim Attribut. Im Beispiel steht der Attributwert in doppelten Anführungszeichen. Die JavaScript-Anweisung darin verwendet einfache Anführungszeichen.
Abbildung 15.1: Rechts: Der Nutzer fährt über die Schaltfläche. Diese wechselt die Farbe (im Druck: die Graustufe)
TIPP
Eine JavaScript-freie Alternative ist der Rollover-Effekt mit der Pseudoklasse a:hover für Links. Hiermit kann man Hintergrundbilder (CSS-Befehl background-image) austauschen.
15.1.1 Rollover-Funktion Der Rollover-Effekt lässt sich auch sehr einfach in Funktionen auslagern. Dadurch wird der Code übersichtlicher: Listing 15.2:
Rollover-Effekt mit zwei Funktionen (rollover_funktion.html)
Rollover
14
15.1.2 Automatisiert
15
Wollen Sie die Funktionen für den Rollover-Effekt mehrmals verwenden, bietet es sich an, mit Parametern beim Funktionsaufruf zu arbeiten, die zeigen, welches Bild jeweils geändert werden soll.
16
Das folgende Beispiel nutzt dies: ■
Zuerst übergibt es eine Nummer an die Funktion.
17
onmouseover="over('1')" ■
Diese wird als Parameter z in der Funktion übernommen. function over(z) {
■
18
Aus dem Parameter und einem Text, der bei allen Schaltflächen gleich ist, wird der Name des Bildes erstellt: var name = "schalt" + z;
■
19
Nun kommt eine weitere Art des Bildzugriffs ins Spiel: die Kollektion3 images[]. Sie ist im Prinzip ein Array mit allen Bildern der Webseite. Die Bilder können entweder mit ihrem Index, z.B. images[0], oder mit ihrem Namen, images["Bildname"], angesprochen werden. Für das Beispiel wird der Bildname durch die Variable name ersetzt:
20 21
document.images[name].src="schalt2.gif";
Ein Direktzugriff über document.name ist hier nicht möglich, da die Variable name nicht als image-Objekt angesehen wird. Der Interpreter sucht, statt den Wert der Variablen zu verwenden, nach einem Bild mit dem name-Attribut name.
22
HALT
23
Hier sehen Sie das vollständige Beispiel: Listing 15.3:
Ein automatisiertes Rollover (rollover_automatisch.html)
24
Rollover
Abbildung 15.2: Jeweils eine Funktion erledigt das Mouseover bzw. Mouseout für alle Schaltflächen.
15.1.3 Automatisiert mit this Bisher wurde immer die Nummer für das jeweilige Bild übergeben. Sie können allerdings aus dem entsprechenden Attribut auch mit dem Schlüsselwort this direkt eine Referenz auf das Objekt übermitteln. Sie haben dann die Möglichkeit, in der Funktion direkt auf das Objekt – in diesem Fall das Bild – zuzugreifen: Listing 15.4:
Die Objekt-Übergabe mit this (rollover_automatisch.this.html)
Rollover img { cursor: pointer;
420
Rollover
}
14 15
15.1.4 Ältere Browser Ältere Browser, sogar Netscape 4.x, unterstützen die Ereignisse onmouseover und onmouseout im -Tag nicht. Hier müssen Sie sie stattdessen in einen Link packen. NS4.x
onmouseover und onmouseout
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Wollen Sie auf sehr alten Browsern, also Netscape 2 und Internet Explorer 3 (Windows), eine Fehlermeldung aufgrund des nicht vorhandenen imageObjekts vermeiden, müssen Sie auch eine Fallunterscheidung einbauen, die testet, ob das Objekt vorhanden ist.
16 Tabelle 15.2: onmouseover und onmouseout im -Tag
18 19
TIPP
20
if (document.images) { Anweisungen; }
21
Das folgende Beispiel wandelt Listing 15.3 ab und macht es auch für ältere Browser kompatibel: Listing 15.5:
17
22
Rollover in einem Link (rollover_aeltere.html)
Rollover in alten Browsern
TIPP
Denken Sie daran, das Attribut border = "0" in das -Tag zu packen, wenn Sie einen Link außen herum legen. Anderenfalls wird das Bild automatisch mit Rahmen in der Linkfarbe angezeigt. Wollen Sie keinen Verweis in den Link einfügen, ersetzen Sie die URL in href durch ein Doppelkreuz (#).4 Damit scrollen allerdings manche Browser bei langen Seiten mit Ankern nach oben.
15.1.5 Zufall Der Zufall ist ein gutes Instrument, um Interaktivität interessant zu machen. Der Nutzer weiß nie, was als Nächstes kommt. Diese Ungewissheit wirkt abwechslungsreich. Im Download-Archiv zum Buch finden Sie im Ordner code\kap15 die Beispielbilder dio.gif und dio1.gif bis dio5.gif. WWW
Eine Zufallszahl zwischen 0 und 1 erzeugen Sie mit Math.random(). Sie lässt sich mit einer einfachen Rechnung in jeden beliebigen Zahlenbereich umwandeln.5 In diesem Beispiel soll beim Rollover eines von fünf verschiedenen Bildern zufällig aufgerufen werden. Der Trick ist, den Namen der Grafik aus einer Zeichenkette ("dio") und der Zufallszahl zwischen 1 und 5 zu bilden. Diese Anweisungen werden in eine Funktion verpackt und nur noch in den bereits bekannten Funktionen over() und out() aufgerufen. Nachfolgend der vollständige Code: Listing 15.6:
Mit Zufall lassen sich schöne Effekte erzielen (rollover_zufall.html).
Rollover mit Zufall
14 15 16 17 18 Abbildung 15.3: Welche Diode erscheint, ist purer Zufall.
19 20 21
Experimentieren Sie ein wenig mit dem Zufall. In diesem Buch wird er Ihnen noch häufiger begegnen, beispielsweise in diesem Kapitel unter 15.4 »Animation?«.
22 TIPP
23
15.1.6 Tipps und Tricks Sie können natürlich bei den onmouseover- und onmouseout-Ereignissen auch ein beliebiges Bild austauschen. Warum sollte sich nicht die Farbe der Überschrift ändern, wenn der Nutzer über eine Schaltfläche fährt? Oder es tauchen zusätzliche Menüpunkte auf. So lässt sich sogar eine Navigation mit Bildern erzeugen.
24
Ebenfalls sehr praktisch ist die Möglichkeit, mehrere Bilder gleichzeitig zu tauschen. So können Sie sogar kompliziertere Schaubilder nur mit Bildern erstellen.
423
Bilder
Bei anderen Ereignissen können Sie natürlich auch Bilder ändern. Ein Beispiel wäre ein Bildwechsel bei onclick. Der Nutzer klickt auf eine Schaltfläche und irgendwo ändert sich ein Bild.
15.2
Vorladen
Die bisher verwendeten Grafiken waren allesamt sehr einfach und klein. Das muss allerdings nicht immer so sein. Wenn Sie viele Grafiken auf der Seite haben oder die Grafiken sehr groß werden, muss der Nutzer auf einen Rollover-Effekt unter Umständen eine Weile warten. Das ist natürlich optisch nicht akzeptabel. Daher können Sie Grafiken vorladen. Dieser Vorgang heißt auch Precaching6 oder Preloading.
TIPP
Precaching ist insbesondere bei Animationen sinnvoll, damit die Animation runder abläuft. Laden Sie Ihre Webseiten auf jeden Fall zum Testen auf den Webserver und probieren Sie dann aus, wie schnell die Seite lädt, wenn Sie eine langsame (also keine DSL- und möglichst keine ISDN-) Verbindung haben. Das Precaching folgt einer ganz einfachen Idee. Zuerst legen Sie ein neues image-Objekt an: var bild = new Image();
Anschließend weisen Sie der Objektinstanz mit der Eigenschaft src als Quelle das Bild zu, das Sie vorladen möchten. bild.src = "schalt2.gif";
Diese zwei Zeilen können Sie nun einfach in eines der Rollover-Beispiele aus dem letzten Abschnitt einfügen. Natürlich ist es auch erlaubt, das Vorladen in einer Funktion zu erledigen. Sie sollten die Funktion allerdings schon im Skriptteil und nicht erst beim onload-Ereignis aufrufen, damit das oder die Bilder sofort vorgeladen werden. Für das folgende Beispiel ist Listing 15.2 die Grundlage. Abgeändert wurde, dass in der Funktion over die Objektinstanz bild statt der Bildquelle angesprochen wird: Listing 15.7:
Vorladen mit dem image-Objekt (vorladen.html)
Vorladen
14 15
Ganz alte Browser, die das image-Objekt noch nicht unterstützen, erlauben dennoch eine Art Vorladen: Sie können Grafiken einfach mit 1 * 1 Pixel Größe in den HTML-Quellcode schreiben:
16
17
Das image-Objekt erlaubt zusätzlich zwei Parameter: die Breite und die Höhe des Bildes in Pixeln. var bild = new Image(Breite, Höhe);
18
INFO
15.2.1 Mehrere Bilder vorladen
19
Bei mehreren Bildern ist das soeben beschriebene Verfahren recht aufwändig. Hier lässt sich ein einfacher Automatismus mit einer Schleife implementieren. Die Bildquellen geben Sie als Array an, die vorgeladenen Bilder landen ebenfalls in einem Array:
20
Listing 15.8:
Mehrere Bilder vorladen (Ausschnitt aus: vorladen_viele.html)
21
var bilder = new Array("dio1", "dio2", "dio3", "dio4", "dio5"); var vorgeladen = new Array(); for (bild in bilder) { vorgeladen[bild] = new Image(); vorgeladen[bild].src = bilder[bild] + ".gif"; }
22 23
Dieses Skript arbeitet folgendermaßen: 1.
Zuerst nimmt das Array bilder alle Bildnamen auf. Im Beispiel wird die Dateiendung erst in der Schleife hinzugefügt. Sie könnte natürlich auch bereits im Array angegeben werden, insbesondere dann, wenn sich die Dateiformate der Bilddateien unterscheiden. 2. Im zweiten Schritt definiert der Code ein neues Array (vorgeladen). 3. Die Schleife geht alle Bildnamen im Array bilder durch. 4. Für jeden Bildnamen wird im Array vorgeladen ein neues image-Objekt instanziiert. 5. Als Quelle erhält dieses Bild den Bildnamen aus dem Array bilder. Angefügt wird die Dateiendung .gif.
24
425
Bilder
TIPP
In unserem letzten Beispiel könnten Sie das Array auch mit einer Schleife füllen und nur in einer Variablen angeben, wie viele Bilder es denn sein sollen. Sie sehen also, dass der Automatisierung in JavaScript kaum Grenzen gesetzt sind!
15.2.2 Alles geladen? Wollen Sie eine komplexere Animation mit JavaScript realisieren, sollten Sie vor ihrem Start überprüfen können, ob bereits alle Bilder geladen sind. Dafür gibt es zwei wichtige Komponenten: ■ ■
Die Eigenschaft complete. Sie ergibt true, wenn ein Bild geladen ist, und kann für jedes Bild überprüft werden. Das Ereignis onload für ein Bild. Es wird aufgerufen, sobald das Bild fertig geladen ist. Diese Eigenschaft muss im Bild selbst stehen, um auch zu älteren Browsern kompatibel zu sein.7 Daher werden die Bilder in der Größe 1 * 1 Pixel in die Seite eingefügt und zusätzlich mit einer Stylesheet-Klasse ausgeblendet.
Aus diesen zwei Komponenten besteht die folgende Überprüfung. Sind alle Bilder geladen, wird auf die Seite index.html weitergeleitet. Listing 15.9:
Das Vorladen testen (vorladen_prüfen.html)
Wurde vorgeladen? .hidden {visibility:hidden}
width="1" height="1" width="1" height="1" width="1" height="1"
14
width="1" height="1"
15
Wenn Sie das Attribut lowsrc verwenden, um ein Bild mit niedrigerer Qualität zuerst zu laden, prüft complete nur, ob dieses Bild geladen wurde. Auch onload tritt bereits ein, wenn das niedrig auflösende Bild fertig geladen ist!
16
TIPP
17
15.2.3 Fehler beim Laden Das im vorigen Absatz geschilderte Verfahren ist allerdings noch nicht ganz perfekt. Zusätzlich können Sie auch die zwei Ereignisse onabort und onerror einsetzen. onabort tritt ein, wenn der Nutzer das Laden anhält, onerror wird bei einem Ladefehler aufgerufen, beispielsweise aufgrund eines fehlerhaften Bildverweises.
18 19
Die beiden Ereignisse existieren, seit es das image-Objekt gibt, also ab Netscape 3 bzw. Internet Explorer 4. Der Firefox und Mozilla haben allerdings damit Probleme. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
onabort
onerror
20 Tabelle 15.3: onabort und onerror
21 22
Das folgende Beispiel ergänzt die zwei Ereignisse zu Listing 15.9:
23
Listing 15.10: In das -Attribut sind die zwei Ereignisse schnell eingefügt (Ausschnitt aus: vorladen_pruefen_fehler.html).
427
Bilder
Natürlich können Sie für das Vorladen auch eigene Funktionen anlegen. In Abbildung 15.4 fehlte eine Grafik. Daher gibt der Browser die Fehlermeldung des onerror-Ereignisses aus. Abbildung 15.4: Beim Laden ist ein Fehler aufgetreten.
15.3
Bilder als Formularelemente
Die HTML-Formulare sind vor allem eines – langweilig. Wer die Radiobuttons und Checkboxen schon hunderte oder tausende Male gesehen hat, findet sie früher oder später ziemlich trist. Eine Alternative ist, die Formularelemente durch Bilder zu ersetzen. Bei Schaltflächen können Sie dies durch das Tag
erreichen. Alternativ reicht eine normale Grafik mit onclick oder bei älteren Browsern mit Link außen herum. Schaltflächen sind aber nicht die einzigen Elemente, die durch eine Grafik ersetzt werden können. Auch Checkboxen und Radiobuttons lassen sich so frei gestalten. Allerdings ist dazu ein wenig mehr Programmierung nötig, wie das folgende Beispiel zeigt. Insbesondere unerfahrene Nutzer haben mit Formularen, die »sehr anders« aussehen, Schwierigkeiten. Achten Sie immer auf gute Usability.8 HALT
8
428
Anglizismus für Benutzerfreundlichkeit. Der Nutzer soll sich schnell zurechtfinden und die Seite einfach und ohne Hilfe bedienen können.
Bilder als Formularelemente
In dem Beispiel sollen zwei Radiobuttons erzeugt werden, die dem Nutzer die Wahl zwischen Kontakt per E-Mail oder per Telefon lassen. Dazu wird eine selbst erstellte Grafik eines leuchtenden roten Buttons erstellt. Ist die Option ausgewählt, soll er grün erscheinen. Radiobuttons haben die Besonderheit, dass immer nur eine Option gleichzeitig ausgewählt sein kann. Für unser Beispiel sollen beide Optionen auch wieder abwählbar sein.9
14 Die zwei Zustände des Buttons finden Sie im Download-Archiv unter opt.gif und opt_on.gif im Ordner code\kap15.
15
WWW
Das Kernproblem dieser Aufgabe ist, dass der aktuelle Zustand der zwei Radiobuttons (welcher ist ausgewählt?) gespeichert werden muss. Er sollte außerdem so gespeichert sein, dass er beim Versenden des Formulars als Formularelement an eine serverseitige Programmiersprache wie beispielsweise PHP, ein CGI oder eine ASP.NET-Anwendung weitergegeben werden kann. Dies funktioniert allerdings nur über einen Umweg, da das Bild selbst kein Formularelement ist. Sie müssen dazu den aktuellen Zustand in ein verstecktes Formularfeld () schreiben. Dieses Formularfeld wird mit dem Formular verschickt und die serverseitige Programmiersprache kann auf den Wert des versteckten Formularfelds zugreifen.
16 17 18
Folgende Schritte sind notwendig: ■
19
Ein leeres, verstecktes Formularfeld enthält den Wert der angeklickten Option.
20
■
Im onclick-Ereignis der beiden Radiobuttons wird die Funktion check mit dem Namen des Bildes als Parameter aufgerufen.
21
Wollen Sie einen Anruf? ■
22
Diese Funktion prüft zuerst, ob der Wert im versteckten Formularfeld dem angeklickten Wert (Parameter, also Name des Bildes) entspricht. Wenn dies nicht der Fall ist, wird das versteckte Formularfeld auf diesen Wert gesetzt, anderenfalls wird es gelöscht. Der zweite Fall tritt also dann ein, wenn der Nutzer ein zweites Mal auf denselben Radiobutton klickt.
23 24
function check(opt) { if (document.formular.kontakt.value != opt) { document.formular.kontakt.value = opt; } else { document.formular.kontakt.value = ""; }
9
Dies ist bei per HTML-Formular erstellten Radiobuttons nicht der Fall.
429
Bilder
■
Abschließend ruft die Funktion check() die Funktion aktualisieren() auf. aktualisieren(); }
■
Diese Funktion ändert zuerst beide Bilder auf den Normalzustand. Anschließend wird der Wert des versteckten Textfelds geprüft. Ist es nicht leer, wird das Bild mit dem Bild für den aktivierten Zustand (grüner Button) ausgetauscht. function aktualisieren() { document.email.src = "opt.gif"; document.tele.src = "opt.gif"; var wert = document.formular.kontakt.value; if (wert != "") { document.images[wert].src = "opt_on.gif"; } }
Hier der vollständige Code: Listing 15.11: Mit Bildern Radiobuttons simulieren (bild_formular.html) Bilder als Formularelemente
Informationen per E-Mail?
Wollen Sie einen Anruf?
430
Bilder als Formularelemente
Abbildung 15.5: Der Nutzer klickt den Radiobutton an …
14 15 16 17 18
Abbildung 15.6: … und die Farbe ändert sich.
19 20 21 22 23
Zwar lassen sich Formularelemente auch mit CSS verändern. Gerade bei Radiobuttons und Kontrollkästchen sind die Möglichkeiten aber beschränkt. Deswegen kommen die hier vorgestellten Bilder als Formularelemente auch in Web 2.0-Anwendungen häufiger vor. Beispielsweise kann so bei einer Registrierung die Frage nach Männlein oder Weiblein mit zwei hübschen Icons gestellt werden.
24 TIPP
431
Bilder
Abbildung 15.7: Wahl zwischen Mann und Frau per Bild in einem Web 2.0-Dienst
15.4 Animation? Die einfachste Art der Animation ist die Animation durch Bildwechsel. Diese Form der Animation machen sich beispielsweise GIF-Animationen zunutze. Ist es aber einmal notwendig, statt GIFs JPEGs zu verwenden – weil mehr Farben erforderlich sind oder Fotos animiert werden sollen –, hilft JavaScript weiter.
432
Animation?
15.4.1 Dateiformate Das Praktische an Bildanimationen mit JavaScript ist, wie bereits erwähnt, dass das Dateiformat keine Rolle spielt. Sie können also eine Animation mit allen drei im Web verbreiteten Bildformaten durchführen: ■
■
■
GIF (Compuserve Graphics Interchange Format) ist das älteste Webformat. Es unterstützt eine Farbtiefe von 8 Bit (entspricht 256 Farben) und komprimiert verlustfrei. Das GIF-Format komprimiert insbesondere flächige Grafiken sehr gut, ist also für Schaltflächen und Ähnliches sehr gut geeignet. Als besondere Funktionen können Sie Teile der GIF-Grafik transparent schalten und eine GIF-Datei als Animation speichern, also mit mehreren Bildern, die hintereinander ablaufen. Das JPEG-Format (Joint Photographic Experts Group) hat eine größere Farbtiefe von 16 Bit (entspricht über 16,77 Mio. Farben), komprimiert allerdings verlustbehaftet; das heißt, je niedriger die Dateigröße werden soll, desto niedriger muss die Qualität gewählt werden. Die Qualitätsverluste äußern sich in Treppenbildung.10 Das PNG-Format (Portable Network Graphics) gibt es in mehreren Varianten: als PNG-8 mit 8 Bit und 256 Farben oder als PNG-24 mit 16 Bit und über 16,77 Mio. Farben. Das PNG-Format komprimiert verlustfrei, bietet ebenfalls Transparenz, aber keine Animation. Das PNG-24 erreicht deutlich höhere Dateigrößen als JPEG, da es nicht verlustbehaftet komprimiert. Dafür unterstützt es Alphatransparenz, sprich halbtransparente Pixel. Leider wird diese nicht vom Internet Explorer 6 und früher unterstützt.11
14 15 16 17 18 19 20
15.4.2 Bildertausch
21
Im Prinzip ist der Bildertausch in der Animation nur eine Fortsetzung des Rollover-Effekts. Die einzige Komponente, die neu hinzukommt, ist der Zeitabstand, in dem die Bilder getauscht werden. Dafür sorgt die Methode setTimeout("Funktion()", Zeit) des window-Objekts. Sie ruft eine Funktion zeitverzögert auf. Die Verzögerung wird im zweiten Parameter Zeit in Millisekunden angegeben.
22 23
setTimeout() gibt es bereits in den Uraltbrowsern Netscape Navigator 2 und Internet Explorer 3 in beiden Versionen. NS4.x
setTimeout()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
24 Tabelle 15.4: Die Methode setTimeout() des windowObjekts
10 Das Bild wirkt so, als hätte es viereckige Punkte; das nennt man auch pixelig. 11 Allerdings gibt es für den Internet Explorer vor 7 einen Hack, damit auch Alphatransparenz möglich wird. Er basiert auf JavaScript und einem Internet Explorer-Verhalten (Behavior). Sie finden ihn unter http://webfx.eae.net/dhtml/pngbehavior/pngbehavior.html. Mittlerweile gibt es im Web außerdem noch einige Varianten.
433
Bilder
Ein einfaches Beispiel illustriert dies: ■
Beim Laden der Seite wird nach 1.000 Millisekunden mit setTimeout() eine Animationsfunktion aufgerufen. Als Parameter wird 0 übergeben. Dieser Parameter legt fest, welches Bild aus dem Bilder-Array zuerst aufgerufen wird. onload="setTimeout('ani(0)', 1000)"
setTimeout() erlaubt die Parameterübergabe von einem oder mehreren Parametern auf zwei Arten: ■
Innerhalb des Funktionsaufrufs, wie in diesem Beispiel gesehen. Wollen Sie hier Variablenwerte verwenden, müssen Sie diese mit dem Namen der Funktion konkatenieren, da dieser ein String ist: setTimeout("ani(" + i + ")", 1000);
■
Als weitere Parameter. Dazu wird die Funktion nicht als String und ohne Klammern übergeben: setTimeout(ani, 1000, i);
■
Die zweite Variante ist allerdings auf den Netscape Navigator ab Version 4.x und Mozilla beschränkt, wird also in der Praxis kaum verwendet. Im nächsten Schritt wird in der Animationsfunktion (ani()) geprüft, ob das letzte Bild bereits erreicht ist. Ist dies nicht der Fall, wird die Bildquelle auf das nächste Bild geändert, i um eins erhöht und die Funktion über setTimeout() nach 1.000 Millisekunden erneut aufgerufen. function ani(i) { if (i < bilder.length) { document.dio.src = bilder[i]; i++; setTimeout("ani(" + i + ")", 1000); } }
Haben Sie es bemerkt? Hier handelt es sich um einen rekursiven Funktionsaufruf (siehe Abschnitt 4.1.7 »Rekursion«). INFO
Natürlich können Sie die Zeitverzögerung, in der die Animation ausgeführt wird, frei wählen und dem Array mehr oder weniger Bilder hinzufügen. Nachfolgend sehen Sie den vollständigen Code: Listing 15.12: Eine einfache Animation (animation.html) Animation
14 15 Abbildung 15.8: Die Animation springt von Bild zu Bild.
16 17 18 19
Bei Animationen sollten Sie die Bilder vorladen (siehe Punkt 15.2 »Vorladen«). Der Übersichtlichkeit halber fehlt das Vorladen bei diesen Arbeitsbeispielen.
20 TIPP
21 Die soeben realisierte Animation ließe sich auch mit einer Schleife konstruieren. Einen Ansatz hierzu finden Sie im Download-Archiv unter animation_ schleife.html.
22
WWW
Endlose Animation
23
Bis jetzt war die Animation auf einen Durchlauf beschränkt. Aus Listing 15.12 lässt sich allerdings schnell eine Endlosanimation machen. Eine einfache if-Fallunterscheidung setzt den Zähler (i) wieder auf 0, wenn das letzte Bild ausgegeben wurde:
24
Listing 15.13: Eine endlose Animation (animation_endlos.html) endlose Animation
Beachten Sie, dass setTimeout() einmal zu viel aufgerufen wird, wenn Sie für die zweite Fallunterscheidung eine else-Anweisung verwenden. INFO
if (i < bilder.length) { document.dio.src = bilder[i]; i++ } else () { i = 0; }
Der Grund: Nach der Erhöhung von i um 1 wird die Fallunterscheidung verlassen, der else-Fall also nicht mehr überprüft. Beim letzten Bild der Animation wäre i also zuerst 4 und würde dann um 1 erhöht. Erst beim nächsten setTimeout()-Aufruf wäre die Bedingung nicht mehr erfüllt. setInterval() setTimeout() ist nicht die einzige Methode des window-Objekts, die sich für Animationen eignet. Die zweite wichtige Methode ist setInterval(Funktion, Zeit). Sie ruft eine Funktion regelmäßig nach einer über den Parameter Zeit angegebenen Zeitspanne in Millisekunden auf. setInterval() ist ein wenig jünger als setTimeout(), da sie erst in der vierten Generation von Netscape und Internet Explorer integriert wurde. Die Methode steht allerdings heute in allen wichtigen Browsern zur Verfügung. Tabelle 15.5: Die Methode setInterval() des window-Objekts
NS4.x
setInterval()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Die endlose Animation aus dem letzten Abschnitt lässt sich auch mit setInterval() realisieren. Dazu wird eine Funktion eingefügt, die den setInterval()-Aufruf enthält.
436
Animation?
Listing 15.14: Die Animation mit setInterval() endlos abspielen
(animation_endlos_setinterval.html). endlose Animation
Überschneiden sich Zeitintervalle wie in diesem Beispiel, kann es zu Fehlern kommen, da der JavaScript-Interpreter zum Abarbeiten der Funktionsaufrufe eine – zwar sehr kurze, aber dennoch vorhandene – Zeitspanne benötigt. Dies ist bei einem kleinen Beispiel wie diesem belanglos, kann aber bei umfangreicherem Code problematisch werden.
15 16 17 18 19 20 INFO
21
Mehrmals ablaufen lassen
22
Eine endlos ablaufende Animation ist natürlich noch nicht der Weisheit letzter Schluss. Besser wäre es, flexibel wählen zu können, wie oft eine Animation abläuft.
23
Um dies zu zeigen, wird Listing 15.13 ein wenig umgearbeitet: ■ ■ ■
24
Eine neue Zählervariable z speichert, wie oft die Animation bisher ablief. Immer wenn i wieder auf 0 gesetzt wird, erhöht sich z um 1, da ein Schleifendurchlauf beendet ist. Die setTimeout()-Anweisung wird nur ausgeführt, wenn z kleiner als 4 ist, also nur bis zum vierten Schleifendurchlauf.
437
Bilder
Hier sehen Sie das komplette Beispiel: Listing 15.15: Eine Animation mehrmals abspielen (animation_mehrmals.html). Animation mehrmals abspielen
Bildertausch mit der Funktion Zufall Um ein wenig mehr Pep in Animationen zu bringen, kommt nun der Zufall ins Spiel. Die Methode zur Gewinnung einer Zufallszahl zwischen 0 und 1, Math.random(), haben Sie bereits kennen gelernt.12 Sie bildet die Basis jeder zufallsgesteuerten Animation. Im folgenden Beispiel verwenden wir die Methode, um fünf Leuchtdioden in zufälliger Reihenfolge in verschiedenen Farben aufleuchten zu lassen. ■
Zuerst wird beim Laden des Dokuments nach einer Zeitverzögerung von 5 Sekunden (= 5.000 Millisekunden) die Animationsfunktion aufgerufen. onload="setTimeout('ani()', 5000)"
■
Diese Funktion ruft mit setInterval() alle 200 Millisekunden die Funktion zufall() auf. function ani() { setInterval('zufall()', 200); }
12 Siehe Abschnitt 6.1.1 »Math«, Abschnitt »Zufallszahlen«.
438
Animation?
■
Die Funktion zufall() gewinnt zwei Zufallszahlen zwischen 1 und 5. Eine Zahl ist für den Dateinamen und die zweite für das Bild im HTMLCode vorgesehen. So wird also bei jedem Aufruf der Funktion eines der fünf Bilder dio1.gif bis dio5.gif zufällig ausgewählt und im nächsten Schritt der Name des Bildes, das ausgetauscht werden soll (dio1 bis dio5).
14
function zufall() { var x = 1 + (Math.round(Math.random() * 4)); var z = 1 + (Math.round(Math.random() * 4)); var neu = "dio" + x + ".gif"; var name = "dio" + z; document.images[name].src = neu; }
15 16
Nachfolgend sehen Sie den kompletten Code: Listing 15.16: Eine einfache Animation mit Zufall (zufall_animation.html)
17
Animation mit Zufall
18 19 20 21 22 23
Abbildung 15.9: Die Lämpchen leuchten fröhlich nacheinander.
439
24
Bilder
Besonderheiten setTimeout() und setInterval() sind praktisch zu handhaben. Dennoch gilt es einiges zu beachten: ■
■
Alle Anweisungen nach setTimeout() und setInterval() werden sofort und nur einmal ausgeführt. Nur der Code in der aufgerufenen Funktion wird zeitverzögert oder mehrmals aufgerufen. setTimeout() und setInterval() können so genannte Identifier erhalten. Dazu müssen sie nur einer Variablen zugewiesen werden. Die Variable enthält dann den Identifier. Er wird verwendet, um die beiden Methoden zu beenden. Dafür gibt es die Methoden clearTimeout() und clearInterval(). var id = setTimeout("Funktion", 1000); clearTimeout(id);
INFO
clearTimeout() und clearInterval() gibt es bereits ebenso lange wie ihre Gegenstücke setTimeout() und setInterval(). Beispiele zu den beiden Methoden finden Sie in den folgenden Abschnitten. Alle vier sind Methoden des window-Objekts.
clearTimeout() Das folgende Beispiel verwendet clearTimeout(), um eine endlos ablaufende Animation zu beenden. Klickt der Nutzer auf die Schaltfläche, wird die Funktion ende() aufgerufen. Sie verwendet clearTimeout() mit der Variablen id als Parameter. Diese globale Variable enthält den Identifier der setTimeout()-Anweisung aus der Animationsfunktion. Beachten Sie, dass id tatsächlich als globale Variable deklariert sein muss. Listing 15.17: Mit clearTimeout() eine Animation stoppen (clearTimeout.html). clearTimeout() Beenden
14 Abbildung 15.10: Die Animation wird angehalten.
15 16 17 18
clearInterval() Das folgende Beispiel verwendet clearInterval(), um die zufallsgesteuerte Animation abzubrechen. Hier wurde der setInterval()-Aufruf nicht in einer Funktion, sondern direkt im Skript ausgeführt. Daher steht die Variable id automatisch als globale Variable zur Verfügung.
19 20
Listing 15.18: Eine Animation mit clearInterval() beenden (clearInterval()). Animation mit Zufall
441
Bilder
Beenden
Abbildung 15.11: Die Schaltfläche hält nun die Animation an.
15.4.3 Größenänderung Die Änderung der Bildgröße ist mit Vorsicht zu genießen. GIF-, PNG- und JPEG-Bilder sind so genannte Bitmap- oder Pixelbilder. Sie bestehen aus einzelnen Bildpunkten oder auch Pixeln.13 Jeder Bildpunkt hat eine Farbe und durch das Aneinanderreihen sehr vieler Bildpunkte entsteht das Bild. Nehmen Sie beispielsweise an, ein Bild hat 200 (Breite) * 100 (Höhe) Pixel. Würden Sie Breite und Höhe verdoppeln, hätte das Bild die Maße 400 * 200 Pixel. Der Haken daran: Irgendwo müssen die zusätzlichen Pixel herkommen. Bei einer Verkleinerung auf 100 * 50 Pixel gibt es ebenfalls einen Haken: Welche Pixel sollen gelöscht werden? Bildbearbeitungsprogramme verwenden dafür die so genannte Interpolation, eine mathematische Berechnung der zusätzlichen oder wegfallenden Bildpixel. Vergrößern Sie dagegen ein Bild im Browser mit den HTML-Attributen width und height oder mit JavaScript, behält das Bild seine Originalgröße und nur die dargestellte Größe wird geändert. Dies geschieht ebenfalls durch Interpolation. Allerdings interpolieren Browser meist wesentlich schlechter als Bildprogramme. Außerdem ist eine nachträgliche Korrektur durch Scharfzeichnen (Schärfen) oder Ähnliches nicht möglich. Das ist verständlich, wenn Sie bedenken, dass Bildbearbeitungsprogramme Spezialisten und Browser eher Mädchen für alles sind.
13 Da Bilder, die für das Internet produziert werden, meist dieselbe Auflösung wie der Bildschirm haben, entspricht ein Pixel des Bildes einem Pixel des Bildschirms. Bilder, die gedruckt werden sollen, benötigen höhere Auflösungen.
442
Animation?
Vektorgrafiken haben das Skalierproblem14 nicht. Da sie aus Punkten, deren Verbindungen und Füllungen bestehen, können sie mathematisch exakt größer oder kleiner gerechnet werden. Allerdings sind Vektorgrafiken auch selten so detailliert wie Pixelbilder. Das bekannteste Vektorgrafik- und Animationsformat im Netz ist Flash (bzw. SWF). Ein neueres standardisiertes und herstellerunabhängiges Format ist SVG (Scalable Vector Graphics).
INFO
14
Sollten Sie nun Größenänderungen im Browser grundsätzlich unterlassen? Wenn es sich vermeiden lässt, auf jeden Fall. Eine Ausnahme bildet die Animation von flächigen und geometrischen Pixelgrafiken. Das folgende Skript ändert die Höhe (Eigenschaft height des image-Objekts) eines 1 Pixel großen Balkens. Eine solche Animation kann z.B. als Basis für einen Ajax-Preloader verwendet werden.
15 16
Der Netscape Navigator 4.x unterstützt height und width zwar, kann sie allerdings nur lesen und nicht setzen. Die folgende Animation funktioniert dort also nicht.
height
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
width
17 Tabelle 15.6: Die Eigenschaften height und width des image-Objekts
Die Animationsfunktion wird mit setInterval() alle 100 Millisekunden aufgerufen. Soll es schneller gehen oder die Animation zügiger ablaufen, wählen Sie einen kleineren Wert, ansonsten einen größeren.
Viele Programmiersprachen animieren mit einem kleinen Flackern zwischen den Bildern, weil erst das alte gelöscht und dann das neue Bild innerhalb einer Millisekunde eingefügt werden muss. Da JavaScript dies vermeidet, sieht eine Animation mit setInterval() »sauber« aus. ■
WWW
21 22 INFO
23
In der Animationsfunktion ändert sich die Höhe des Bildes um 1. Wählen Sie einen größeren Wert, wenn die Animation schneller ablaufen soll.
24
i++; document.balken.height = i; ■
19 20
Den Balken finden Sie unter balken.gif im Download-Archiv. ■
18
Die Breite muss ebenfalls gesetzt werden, auch wenn sie gleich bleibt. Ansonsten würde der Balken proportional in Höhe und Breite skaliert. document.balken.width = 4;
14 Unter Skalieren versteht man die Änderung der Bildgröße.
443
Bilder
■
Die if-Anweisung testet, wie hoch die Grafik bereits ist. Als Maß verwendet das Skript die Eigenschaft availHeight und zieht von ihr 200 Pixel ab. availHeight gibt die Größe des Platzes auf dem Bildschirm an, den der komplette Browser (inklusive Leisten) einnehmen kann. Elemente wie die Windows- oder KDE-Taskleiste werden abgezogen. if (i >= screen.availHeight-200)
TIPP
Im Netscape Navigator 4.x ist screen ein eigenständiges Objekt und muss daher ohne vorangehendes window. geschrieben werden. Da Sie window in den anderen Browsern einfach weglassen können, sollten Sie dies also auf jeden Fall tun. Eine Alternative wäre window.innerHeight. Diese Eigenschaft gibt die Höhe des Raums im Browser an, der für das Dokument reserviert ist. Allerdings gibt es diese Eigenschaft nur für Netscape ab Version 4.x. ■
Ist i, die Zählervariable, bereits zu hoch, löscht clearInterval() das Animationsintervall. clearInterval(id);
Hier der komplette Code: Listing 15.19: Eine einfache Größenanimation (animation_groesse.html) Animation mit Bildgröße = screen.availHeight-200) clearInterval(id); } function start() { id = setInterval('ani()', 100); } //-->
WWW
444
Im Download-Archiv finden Sie unter dem Namen code\kap15\animation_ groesse_setTimeout.html das Skript mit derselben Funktionalität nur unter Verwendung von setTimeout().
Formatierung und Verschönerung
Abbildung 15.12: Die Bildgröße wird erhöht und die Animation läuft von oben nach unten.
14 15 16 Wollen Sie auf Ihrer Seite Thumbnails (kleine Bilder) anbieten, die beim Anklicken größer gezoomt werden, sollten Sie nicht die Größenskalierung verwenden. Sie sollten vor allem nicht die Bilder für die Übersicht verkleinern, da die große Variante dennoch geladen werden muss. Tauschen Sie stattdessen bei einem Mausklick das größere mit dem kleineren Bild und geben Sie für das neue Bild die (großen) Originalmaße an.
17 TIPP
18 19
15.4.4 Tipps und Tricks
20
Es gibt auch eine dritte Art der Animation: die Positionsänderung. Sie erfolgt – auch für Bilder – über Stil-Anweisungen (CSS) und fällt unter den Oberbegriff DHTML. Mehr dazu erfahren Sie in Kapitel 22 »CSS und JavaScript« und in den diversen Kapiteln rund um DHTML-Anwendungen.
21
Die verschiedenen Animationsarten lassen sich natürlich auch mischen. So können Sie beispielsweise eine Größenanimation mit einem Bildtausch verbinden oder den Nutzer Bilder per Drag&Drop platzieren lassen, die beim Anklicken die Farbe wechseln.
22 23
15.5 Formatierung und Verschönerung
24
Das -Tag besitzt einige Attribute, die das Aussehen und das »Drumherum« steuern. Vier davon sind besonders interessant, da sie auf mehreren Browsern verfügbar sind: ■ ■ ■ ■
align – erlaubt, die Ausrichtung von Text an einem Bild zu ändern. Die Eigenschaft entspricht dem align-Attribut des -Tags. border – gibt die Rahmendicke in Pixeln an. hspace – bestimmt den horizontalen Abstand neben dem Bild. vspace – legt den vertikalen Abstand fest.
445
Bilder
In Netscape 4.x sind all diese Attribute nur les- und nicht schreibbar. Tabelle 15.7: Weitere Eigenschaften des image-Objekts
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
align
()
border
()
hspace
()
vspace
()
Abbildung 15.13: Das Ausgangsbild
Das folgende Beispiel setzt alle vier Eigenschaften ein. Sie sehen die Ausgangssituation in Abbildung 15.13. Der Code besteht nur aus einem Bild mit zugehörigem Text: Drück mich
Als erste Änderung wird im onload-Ereignis eine Funktion format() aufgerufen. Sie ändert nun die vier Eigenschaften: Listing 15.20: Bildattribute ändern (attribute.html) Die Eigenschaften für Bild-Attribute function format() { document.schalt.align = "middle"; document.schalt.border = 2; document.schalt.hspace = 6; document.schalt.vspace = 10; } Drück mich
446
Formatierung und Verschönerung
Abbildung 15.14: Das Ausgangsbild wurde kräftig durcheinandergewirbelt
14 15 Die vier Eigenschaften lassen sich auch dann mit JavaScript ändern, wenn sie bereits im -Tag definiert sind. Allerdings bietet sich in moderneren Browsern eher der Einsatz von CSS-Formatierungen an (siehe Kapitel 22 »CSS und JavaScript«).
16 INFO
17
15.5.1 Alternativtext
18
Der Alternativtext steht in HTML immer im alt-Attribut. Der Text wird angezeigt, wenn eine Grafik nicht dargestellt werden kann15, oder als HilfetextKasten, wenn der Nutzer mit der Maus über die Grafik fährt. Außerdem ist er für die Barrierefreiheit wichtig, da er beispielsweise von einem Screenreader vorgelesen werden kann. Hier ist allerdings immer die Frage, ob der Screenreader JavaScript interpretieren kann. Aus Gründen der Barrierefreiheit sollte das alt-Attribut deswegen am besten direkt im -Tag gesetzt werden.
19 20
In JavaScript greifen Sie mit der Eigenschaft alt des image-Objekts auf den Alternativtext zu und können ihn auch ändern. Sie sollten diesen Text immer angeben, da sich ansonsten insbesondere Nutzer mit deaktivierten Grafiken ärgern.
21 22
In Netscape 4.x ist die alt-Eigenschaft nur les- und nicht schreibbar.
alt
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
()
Tabelle 15.8: Die alt-Eigenschaften des image-Objekts
Das folgende Beispiel fügt nachträglich einen Alternativtext hinzu: Listing 15.21: Alternativtext mit JavaScript (alt.html) Alternativtext function alt() { 15 Mögliche Gründe sind meist deaktivierte Grafiken oder ein fehlerhafter Link.
447
23 24
Bilder
document.schalt.alt = "Klick mich!"; }
TIPP
Wenn Sie bei einer Animation aus mehreren Bildern für jedes Bild einen eigenen Alternativtext wünschen, hilft die alt-Eigenschaft weiter. Sie können die Alternativtexte ebenso wie die Bildnamen in einem Array speichern und mit einer Schleife auslesen.
15.6 Imagemaps und JavaScript Für Imagemaps gibt es drei interessante JavaScript-Anwendungen: ■
■
In href-Attributen der -Tags können Sie auch JavaScript-Funktionen aufrufen. Dies funktioniert genauso wie bei gewöhnlichen Links und wird daher hier nicht erläutert. In Imagemap-Bereichen, also -Tags, stehen auch Ereignisse wie onmouseover und ommouseout zur Verfügung. Sie können also beispielsweise beim Überfahren eines Imagemap-Bereichs das komplette Bild ändern.
Opera in Version 6 und frühere Versionen hatten beim onmouseout-Ereignis einen Bug, sodass Rollover-Effekte nicht mehr funktionierten. TIPP
■
Mit der Eigenschaft useMap des image-Objekts weisen Sie einem Bild eine Imagemap zu. Der Name der Imagemap folgt in Klammern mit vorangestelltem Doppelkreuz (#).16
Im Opera kann useMap nicht gesetzt, sondern nur gelesen werden. Das Beispiel scheitert hier also. Tabelle 15.9: Die Eigenschaft useMap des image-Objekts
WWW
NS4.x
useMap
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
()
Das folgende Beispiel verwendet ein einfaches Bild grafik.gif, das aus zwei Farbflächen besteht. Sie finden es im Download-Archiv im Ordner code\ kap15.
16 Auch Hash.
448
M/FF
Imagemaps und JavaScript
Dieses Bild soll mit einer Imagemap versehen werden. Der rote Bereich links verweist auf die Webadresse von Markt+Technik (oder eine beliebige andere Seite), der rechte blaue auf die von Hauser & Wenz. Diese Zuordnung soll sich drehen, wenn der Nutzer eine Schaltfläche anklickt. Der Lösungsansatz ist so gut wie einfach: Sie legen zwei Imagemaps an und wechseln mit useMap die Imagemap. Dazu ist eine einfache Fallunterscheidung mit einer if-Anweisung ausreichend:
14
function map() { if (document.grafik.useMap == "#karte") { document.grafik.useMap = "#karte_gedr"; } else { document.grafik.useMap = "#karte"; } }
15 16
Hier der komplette Code:
17
Listing 15.22: Mit useMap Imagemaps tauschen (usemap.html) usemap Drehen
18 19 20 21 22 23 24
449
Bilder
Um den Effekt des Beispiels zu überprüfen, müssen Sie natürlich wissen, wohin der Link führt. Sie sehen ihn in der Statusleiste des Browsers. INFO
Abbildung 15.15: Links ist der Link auf Markt+Technik (siehe Statusleiste).
Abbildung 15.16: Nach Anklicken der Schaltfläche DREHEN ist links nun der Link auf Hauser & Wenz.
450
Inhalt
16 Navigationshilfen 14
In diesem Kapitel sind kleine, aber wertvolle Skripte versammelt, die alle dabei helfen, Ihre Seiten besser nutzbar zu machen. Als wichtige Objekte kommen gleich zu Beginn das history-Objekt für die Browser-History und das location-Objekt für Weiterleitungen zum Einsatz. Den Links und dem link-Objekt ist ein eigener Abschnitt gewidmet. Anschließend geben Sie Text in der Statusleiste aus, erfahren, wie Laufschriften für Newsticker funktionieren, und integrieren eine Druckfunktion mit JavaScript.
15
16.1
18
16 17
history-Objekt
Das history-Objekt enthält die History des Browsers, also die zuletzt besuchten Seiten. Es ist ein Unterobjekt von window; richtig heißt es also:
19
window.history
Allerdings kann window auch problemlos weggelassen werden.1 Mit den Methoden dieses Objekts können Sie die ZURÜCK- und VORWÄRTS-Schaltflächen2 Ihres Browsers per JavaScript simulieren: ■
■
back() – die zuletzt besuchte Seite. forward() – die nächste Seite in der History. Diese Seite ist immer dann vorhanden, wenn Sie vorher mit back() oder der Browserschaltfläche ZURÜCK in der History nach hinten gegangen sind. go(Ort) – springt auf eine bestimmte Seite in der History, die bei Ort angegeben ist. Die Angabe erfolgt als ganze Zahl. 1 geht also eine Seite in der History nach vorne, –1 eine Seite nach hinten. Alternativ kann auch eine URL oder der Seitentitel angegeben werden. Wenn und nur wenn er in der History vorhanden ist, springt der Browser dorthin.
Mit go(0) laden Sie die aktuelle Seite neu. Allerdings je nach Browser entweder aus dem Cache (Zwischenspeicher) oder durch erneutes Anfordern vom Server. Letzteres finden Sie häufig beim Internet Explorer.
1
2
21
22
23
24
INFO
In manchen Fällen, beispielsweise beim screen-Objekt, ist dies sogar sinnvoll, da der Netscape Navigator 4.x screen nur als eigenes Objekt kennt. Bei anderen Objekten wie document kann ein Weglassen dagegen zu Fehlern führen. Die Bezeichnungen für diese Schaltflächen variieren. Der Firefox verwendet beispielsweise EINE SEITE VOR statt VORWÄRTS.
451
ndex
■
20
Navigationshilfen
■
length – gibt an, wie viele Elemente in der History sind. Daraus lässt sich leider nicht viel schließen, da der Nutzer beliebig mit dem Browser, über die Schaltflächen VORWÄRTS und ZURÜCK und mit Menübefehlen, in der History navigieren kann. Der Ausgangspunkt, das heißt die aktuelle Position in der History, ist also nicht feststellbar. Einzige Ausnahme: Wenn die Länge der History 1 ist, handelt es sich um die erste, in der History geladene Seite.
Das folgende Beispiel setzt die drei Methoden ein, um per Link die Navigationsschaltflächen des Browsers nachzubauen: Listing 16.1:
Eine selbst gebaute Browsernavigation (history.html)
Die History Zurück Vorwärts Zwei zurück
Die Angabe return false für die Zurück-Schaltfläche wird hier für den Firefox und Mozilla benötigt, damit der Link auf den Hash nicht ausgeführt wird. In der Praxis wird die selbst definierte History nur selten verwendet, da die meisten Nutzer die Browserschaltflächen gewohnt sind. TIPP
Abbildung 16.1: Die Browsernavigation in der HTML-Seite
452
Weiterleitung und das location-Objekt
Bei Frames geht der Internet Explorer nur mit den URLs in der Adressleiste zurück. Mehr dazu erfahren Sie in Kapitel 19 »Frames«. REF
16.1.1 Mozilla und Netscape Navigator Der Netscape Navigator ab Version 4 und der Mozilla besitzen drei Eigenschaften, die den anderen Browsern fehlen: ■ ■ ■
14
current – die URL der aktuellen Seite next – die URL der Seite, die sich als nächste Seite (Schaltfläche VORWÄRTS) in der History befindet previous – die URL der zuletzt besuchten Seite
15 16
Diese Eigenschaften wirken auf den ersten Blick recht unsicher. Oder können Sie sich vorstellen, dass es erbaulich ist, wenn eine beliebige Website immer erfährt, von welchem Ort der Nutzer auf diese Seite surft?
17
Allzu große Sorge ist allerdings unbegründet. Diese Informationen werden nur an so genannte Signed Scripts, also Skripte mit Zertifikat, oder nach der Bestätigung eines Browserdialogs ausgegeben. Der Nutzer muss also auf jeden Fall zustimmen.
18 Abbildung 16.2: Der Browser verweigert die Erlaubnis.
19 20 21 22
Mehr zum Thema Sicherheit erfahren Sie in Kapitel 30 »Sicherheit«. REF
16.2 Weiterleitung und das location-Objekt
23
Das location-Objekt dient hauptsächlich zur Weiterleitung auf eine andere Seite, weil das Objekt eigentlich alles rund um die URL der aktuell im Fenster geöffneten Seite enthält.
24
Zur Weiterleitung dient nur eine Eigenschaft des Objekts: href. Sie erhält als Wert die neue Adresse, die relativ oder absolut angegeben werden kann. Die Umleitung erfolgt sofort:
453
Navigationshilfen
Listing 16.2:
Umleiten mit href (href.html)
Seite umleiten Auf Markt+Technik umleiten
Alternativ zu href können Sie auch die Methode assign("URL") verwenden. Sie hat dieselbe Wirkung, ist aber in der Praxis weniger in Gebrauch.3 window.location.assign("http://www.mut.de/");
Die Eigenschaft href und die Methode assign() gibt es bereits in den sehr alten Browsern Netscape 2 und Internet Explorer 3 in beiden Varianten. Tabelle 16.1: href und assign()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
href
assign()
16.2.1 Zeitversetzt umleiten Um erst nach einer festlegbaren Zeitspanne umzuleiten, eignet sich am besten setTimeout(). Im folgenden Beispiel startet ein Anklicken der Schaltfläche die Umleitung. Im Textfeld wird die Wartezeit durch eine kleine Animation verkürzt, indem jede Sekunde (setInterval()) ein Punkt ausgegeben wird: Listing 16.3:
Zeitversetzt umleiten (umleitung_zeitversetzt.html)
Seite umleiten Wartezeit
14 15 16 17 Abbildung 16.3: Die Wartezeit wird mit einer Anzeige der bereits vergangenen Sekunden »belohnt«.
18 19 20 21
16.2.2 Anker usw. Einige Eigenschaften des location-Objekts liefern nützliche Informationen über die URL bzw. lassen sich auch einzeln setzen: ■ ■ ■ ■ ■ ■ ■
22
hash – enthält den Teil der URL nach dem Doppelkreuz (#, auch Hash). Damit haben Sie direkten Zugriff auf den Anker. hostname – liefert den Namen des Hosts ohne Port, beispielsweise www.mut.de. host – gibt den Host mit Port zurück, beispielsweise www.beispiel. de:80. port – enthält nur den Port, beispielsweise den Standard-Webport 80. pathname – steht für den Pfad einer Adresse, beispielsweise /daten/index.html. protocol – enthält das Protokoll, also http:, ftp: oder Ähnliches. search – liefert den Suchstring hinter dem Fragezeichen (?) in der URL.
23 24
455
Navigationshilfen
Alle Eigenschaften sind bereits in sehr alten Browsern ab Netscape 2 und Internet Explorer 3 in beiden Varianten verfügbar. Tabelle 16.2: Eigenschaften des locationObjekts
location
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
16.2.3 Neu laden Die Methode reload() des location-Objekts sieht auf den ersten Blick sehr praktisch aus. Sie besitzt zusätzlich einen Parameter, der angibt, ob die Seite vom Server oder aus dem Browsercache geladen werden soll. reload(true);
gibt an, dass die Seite vom Server neu angefordert werden soll. Eine Version im Cache soll ignoriert werden. reload(false);
holt die Seite aus dem Browsercache, wenn sie dort vorhanden ist – ansonsten vom Server. Diese Methode wird auch verwendet, wenn der Parameter fehlt. Das Problem mit reload() ist, dass das Verhalten des Browsercache nicht standardisiert ist. Daher ist es nahezu unvorhersagbar, ob der Browser tatsächlich so reagieren wird, wie per reload() gewünscht. Die Methode replace("URL") leitet den Nutzer auf eine neue Seite weiter, ohne dass die alte in der History verbleibt. reload() und replace() sind in älteren Browsern ab Netscape 3 und Internet Explorer 4 verfügbar. Tabelle 16.3: reload() und replace()
NS4.x
reload() und replace()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Die Methode go() des history-Objekts lädt mit history.go(0);
die Seite neu, ohne Scrollposition und Einträge in Formularelementen zu verlieren. Nur globale Variablen und Funktionsaufrufe, beispielsweise mit setInterval() oder setTimeout, werden gestoppt. Zum Testen fügen Sie folgenden Code in Listing 16.3 ein: Listing 16.4: Neu laden mit go() (go.html) function los() { setTimeout("umleiten()", 10000); id = setInterval("ausgeben()", 1000);
456
Links
setInterval("neu laden()", 3000); } . . . function neu laden() { history.go(0); }
14 Abbildung 16.4: Das Skript wird bei wenigen Punkten gestoppt.
15 16 17 18 19
16.3 Links Links dienten in den bisherigen Kapiteln hauptsächlich dazu, JavaScriptCode aufzurufen. Neben dieser Möglichkeit können Sie allerdings auch noch auf einige Eigenschaften von Links zugreifen. Der Zugriff auf einen Link erfolgt meist auf einem der drei folgenden Wege: ■
M/FF
IE4
getElementById()
■
21
Über die ID per getElementById(ID). Dies funktioniert nur in neueren Browsern. NS4.x
■
20
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 16.4: getElementById()
Über document.all.ID für den Internet Explorer 4 (und höher) Über die Kollektion document.links[Index], die alle Links eines Dokuments beinhaltet. Sie enthält entweder den Index oder den Namen (name-Attribut) des Links. Dies funktioniert auch in älteren Browsern.
document. links
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
22 23 24
Tabelle 16.5: document. links
457
Navigationshilfen
16.3.1 Linkziel ändern Die zwei Eigenschaften href und target für -Tags sind besonders interessant. Allerdings ist hier Vorsicht geboten, da diese Eigenschaften nur für echte Links gelten, also -Tags mit href-Attribut, nicht aber für Anker, die ohne href-Attribut auskommen. Die href-Eigenschaft des link-Objekts gibt es bereits in sehr alten Browsern ab Netscape 2 und Internet Explorer 3 in beiden Versionen. Tabelle 16.6: href-Eigenschaft href
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel greift mit document.links[] auf den ersten Link im Skript mit dem Index 0 zu und ändert dann – je nach vorher angegebener URL – mit href das Ziel des Links. Listing 16.5:
Mit href den Link ändern (link.html)
Zugriff auf einen Link Wechselnder Link Link ändern
Die Links sollten mit schließendem Schrägstrich angegeben werden, da der Firefox und Mozilla die URLs in dieser korrekten Schreibweise erwarten. TIPP
Informationen über die URL, auf die verwiesen wird, erhalten Sie mit denselben Eigenschaften wie beim location-Objekt (siehe Seite 453). REF
458
Links
Abbildung 16.5: Zuerst geht der Link auf Hauser & Wenz …
14 15 16 Abbildung 16.6: … und dann auf Markt+Technik.
17 18 19 20
target
21
Die target-Eigenschaft entspricht dem target-Attribut aus HTML. Sie steuert, in welchem Fenster ein Link geöffnet wird. Folgende Werte sind möglich: ■ ■ ■ ■ ■ ■
22
_top – öffnet den Link im obersten Fenster. Alle Frames und Framesets werden verlassen. _blank – öffnet den Link in einem neuen, leeren Fenster. _parent – öffnet den Link im übergeordneten Fenster. Bei einem verschachtelten Frameset muss das nicht das oberste Fenster sein. _self – öffnet den Link in demselben Fenster oder Frame. Beliebiger Name – gibt es bereits ein Fenster oder einen Frame mit diesem Namen, wird der Link darin geöffnet. Beliebiger Name – gibt es noch kein Fenster oder keinen Frame mit diesem Namen, öffnet sich der Link in einem neuen Fenster, das diesen Namen erhält.
23 24
459
Navigationshilfen
16.3.2 Anker Für Anker gibt es wesentlich weniger Eigenschaften. Die einzig wirklich interessante und in verschiedenen Browsern verfügbare ist das name-Attribut. Hiermit können Sie den Namen eines Ankers ändern. Anker befinden sich allerdings nicht in der Kollektion document.links[]. Daher müssen Sie für den Zugriff getElementById() oder document.all verwenden. Das heißt, das name-Attribut ist zwar im Netscape 4.x vorhanden, kann aber nur für Links verwendet werden, da die anderen Zugriffsarten auf Anker scheitern. In den älteren Browsern Netscape 2 und 3 sowie im Internet Explorer 3 in beiden Varianten ist name nicht vorhanden. Tabelle 16.7: name-Eigenschaft name
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel liest die name-Eigenschaft für einen Anker aus. Dabei hilft eine einfache Fallunterscheidung der unterstützten Browserobjekte: Listing 16.6: Die anker-Eigenschaft eines Ankers auslesen (anker.html) Zugriff auf einen Anker Anker
Abbildung 16.7: Der Ankername wird korrekt ausgegeben.
460
Statusleiste
Eine sinnvolle Anwendung der name-Eigenschaft von Ankern in der Praxis könnte sein, bei längeren HTML-Dokumenten die Rücksprungmöglichkeiten mit JavaScript zu verwalten. Ebenfalls sinnvoll ist der Einsatz im AjaxUmfeld, wenn damit der Einsatz der Zurück-Schaltfläche ermöglicht werden soll.
TIPP
14 16.4 Statusleiste
15
Die Statusleiste finden Sie im Browser (meist) ganz unten.4 Ist die Statusleiste ausgeblendet, finden Sie sie in den meisten Browsern im Menü ANSICHT (Internet Explorer, Opera, Firefox) oder in englischen Versionen des Browsers in der jeweiligen englischen Entsprechung.
16
Die Statusleiste enthält normalerweise nützliche Informationen über Elemente auf der HTML-Seite. Hauptsächlich handelt es sich hierbei um das Linkziel, wenn der Nutzer über einen Hyperlink fährt.5
17
In JavaScript ist die Statusleiste eine Eigenschaft von window, nämlich status. Mit ihr können Sie den Text in der Statusleiste ändern:
18
window.status = "Neuer Text";
Die Eigenschaft status gibt es in allen älteren und neueren Browsern. Der Firefox schaltet allerdings das Ändern des Textes in der Statuszeile standardmäßig in den Einstellungen aus.
status
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
()
19 Tabelle 16.8: status-Eigenschaft
Abbildung 16.8: Im Firefox ist das Ändern des Textes in der Statusleiste standardmäßig deaktiviert.
20 21 22 23 24
Dies führte zur Idee, beim Überfahren von Links die Adresse durch einen beschreibenden Text in der Statusleiste zu ersetzen. 4 5
Meist, weil die Statusleiste im Opera oben oder unten angezeigt werden kann. Wird das Fenster sehr klein, zeigt der Internet Explorer den Text in der Statusleiste nicht mehr an. Der Netscape Navigator blendet die Statusleiste bei einem zu kleinen Browserfenster automatisch aus.
461
Navigationshilfen
HALT
Viele Nutzer bevorzugen es, wenn sie sehen, wohin ein Link führt. So können sie beispielsweise feststellen, ob sie den Link in einem neuen Fenster öffnen können oder, weil es ein JavaScript-Link auf ein Popup-Fenster ist, dies nicht möglich ist. Im folgenden Beispiel ändert sich die Statusleiste, wenn der Nutzer mit der Maus über den Link fährt: Listing 16.7:
Die Statusleiste ändern (status.html)
Statusleiste Markt+Technik
Im Listing fällt Ihnen sicher die Anweisung return true nach der Änderung der Statusleiste auf. Da der Link seine URL direkt in die Statusleiste schreiben würde, dies in der Rangfolge aber erst nach dem Ereignis ausgeführt wird, würde der Text in der Statusleiste nach sehr kurzer Zeit durch die URL überschrieben. Dies verhindert return true. Abbildung 16.9: Der Text wird unten in der Statusleiste angezeigt
16.4.1 Automatisieren Benötigen Sie die Statusleisten-Änderung für mehrere Links6, können Sie sie natürlich auch komfortabel in eine Funktion packen. Hier ist allerdings return true in der Funktion wichtig. 6
462
Die Anwendung ist natürlich nicht auf Links beschränkt. Sie könnten die Funktion auch im onloadEreignis, von einem Bild usw. aufrufen.
Statusleiste
function ausgeben(text) { window.status = text; return true; }
Beim Aufruf muss das Rückgabeergebnis der Funktion (true) noch einmal mit return zurückgegeben werden.
14
Wenn Sie dies vergessen, überschreibt die URL den Text in der Statusleiste!
15
Hier der komplette Code: Listing 16.8: Den Text in der Statusleiste automatisiert über eine Funktion ändern.
16
(status_funktion.html) Statusleiste mit Funktion Markt+Technik Hauser und Wenz
17 18 19 20 21 Abbildung 16.10: Auch die Änderung über eine Funktion funktioniert reibungslos.
22 23 24
463
Navigationshilfen
16.4.2 Laufschrift Der Begriff Laufschrift steht für bewegten Text, der von links nach rechts oder von rechts nach links in der Statusleiste läuft. Laufschriften waren bei ihrem ersten Auftauchen – wie so vieles – große Mode, später allerdings sehr out. Die große Zeit der Laufschriften ist also sicherlich vorbei, aber gerade weil sie nur noch so selten zum Einsatz kommen, können Laufschriften bei passender Gelegenheit ein Ausrufezeichen setzen. Im Netz kursiert eine Reihe von Skripten zu Laufschriften. Das Grundprinzip ist jedoch überall gleich. Mit setTimeout() oder setInterval() wird die Funktion, die den Text der Statusleiste setzt, immer wieder in regelmäßigen Abständen aufgerufen. Bei jedem Aufruf ändert sich die Ausgabe in der Statusleiste, sodass der Eindruck von Bewegung entsteht. Das folgende Beispiel illustriert dies praktisch. Dabei wird ein Text unablässig in der Statusleiste von links nach rechts bewegt: ■
Zu Beginn stehen immer einige Variablendefinitionen. Der Text steht in einer eigenen Variablen text. Die Länge des Textes wird ebenfalls in einer eigenen Variablen (laenge) aufbewahrt. Als Nächstes folgt eine Arbeitsvariable ausgabe, die immer die aktuelle Ausgabe in der Statusleiste aufnehmen soll. Den Abschluss bildet die Variable id, die den Identifier von setTimeout() aufnehmen wird. var text = " --Kursfeuerwerk des DAX---Wachstumsmarkt JavaScript-- "; var laenge = text.length; var ausgabe = text; var id;
INFO
Theoretisch käme dieses Beispiel auch nur mit der Variablen für den Text aus, könnte die Länge direkt daraus ziehen und sie auch als Variable für die Ausgabe verwenden. Dies ist allerdings ein wenig unübersichtlicher. ■
Die Funktion für die Laufschrift teilt den Text. Das erste Zeichen (Position 0) wird vorne mit substring(1, laenge) ausgeschnitten und hinten wieder angefügt.7 Dies sorgt für die Bewegung. function laufschrift() { ausgabe = ausgabe.substring(1, laenge) + ausgabe.substring(0,1);
■
Der geänderte Text landet in der Statusleiste: window.status = ausgabe;
■
Nun ruft sich die Funktion mit setTimeout() selbst wieder auf. Den Zeitabstand und damit die Geschwindigkeit der Laufschrift können Sie flexibel ändern. id = setTimeout("laufschrift()", 80); }
7
464
Wenn Sie – wie in diesem Fall – bis zum Ende des Strings schneiden möchten, reicht auch ausgabe. substring(1), die längere Variante ist allerdings etwas leichter zu lesen.
Statusleiste
■
Die Funktion laufschrift() muss nun noch im onload-Ereignis des -Tags aufgerufen werden.
An dieser Stelle ist das Beispiel eigentlich bereits komplett, wenn die Seite keine Links hat, die einen eigenen Text in der Statusleiste ausgeben möchten. Da dies allerdings meist der Fall ist, besitzt das Skript auch eine Funktion anhalten(), die die Laufschrift anhält und durch einen anderen Text für die Statusleiste ersetzt. ■
14
Die Funktion wird im onmouseover-Ereignis aufgerufen. Verlässt der Nutzer den Link mit der Maus, soll natürlich wieder die Laufschrift starten. Dazu wird die laufschrift()-Funktion erneut aufgerufen. Da die Variablen gespeichert wurden, macht sie an der Position weiter, an der sie aufgehört hatte.
15 16
Markt+Technik ■
17
Die Funktion zum Anhalten unterbricht mit clearTimeout() das erneute Aufrufen der laufschrift()-Funktion. Daher ist es wichtig, dass der Identifier von setTimeout() vorher einer Variablen (hier id) zugewiesen wurde.
18
function anhalten(linktext) { clearTimeout(id); ■
19
Anschließend gibt die Funktion den als Parameter übermittelten Text zum Link aus und liefert true zurück, damit der Text nicht von der URL überschrieben wird.
20
window.status = linktext; return true;
21
}
Wenn Sie in der Statusleiste die URL des Links anzeigen möchten, setzen Sie sie statt auf einen Text auf einen leeren String: window.status = "";
22
INFO
Hier der komplette Quellcode im Überblick:
23
Listing 16.9: Eine einfache Laufschrift (status_laufschrift.html) Laufschrift in der Statusleiste Markt+Technik
Abbildung 16.11: Die Laufschrift läuft durch …
Abbildung 16.12: … und wird nur angehalten und ersetzt, wenn der Nutzer über einen Link fährt.
466
Statusleiste
Die ruckelige Animation rührt daher, dass verschiedene Buchstaben und Leerzeichen in den meisten Schriften unterschiedlich breit sind. Da sich die Schrift der Statusleiste nicht ändern lässt, gibt es hier keine Abhilfe. Wollen Sie es ruckelfrei, müssen Sie stattdessen Laufschrift in einem Textfeld einsetzen (siehe Abschnitt 16.5 »Laufschrift«).
INFO
14
Laufschrift umkehren Möchten Sie, dass die Laufschrift statt von rechts nach links lieber genau umgekehrt, also von links nach rechts, läuft, müssen Sie nur statt des ersten Buchstabens immer den letzten hinten anhängen.
15
Im Beispiel aus Listing 16.9 müssen Sie nur eine Zeile ändern:
16
Listing 16.10: Die Laufschrift in der Gegenrichtung (status_laufschrift_andersherum.html) ausgabe = ausgabe.substring(laenge-1,laenge) + ausgabe.substring(0, laenge-1);
17
Laufschrift komplex
18
Die letzten Beispiele waren angenehm einfach. Dafür erscheint derselbe Laufschrifttext auch mehrmals und läuft nicht sehr lange durch. Um das zu beheben, könnten Sie in der Ausgabe zusätzlich Leerzeichen vor den Text einfügen.
19
Im folgenden Beispiel soll der Text von links nach rechts laufen. Dazu sind folgende Programmelemente notwendig: ■
var var var var var ■
20
Bei der Variablendefinition ist die Variable leer hinzugekommen. Sie nimmt die eingefügten Leerzeichen auf. Die Variable pos speichert die Startposition, ab der der Text noch sichtbar ist. Ganz zu Anfang soll der Text komplett unsichtbar sein, daher entspricht die Startposition der Textlänge. Die Variablen ausgabe und id sind noch nicht initialisiert, haben also noch keinen Wert. Sie sollen später den auszugebenden Text beziehungsweise den Identifier von setTimeout() aufnehmen. text = "--Kursfeuerwerk des DAX-leer = ""; pos = text.length; ausgabe; id;
21 22
--Wachstumsmarkt JavaScript--";
23 24
Der ausgegebene Text (Variable ausgabe) besteht aus allen Buchstaben des Textes von der Startposition (pos) bis zum Ende. Die Startposition wird flexibel geändert und ergibt die Bewegung im Text. function laufschrift() { ausgabe = text.substring(pos, text.length);
■
Nun gilt es zwei Fälle zu unterscheiden. Wenn der komplette Text dargestellt wird, hat pos den Wert 0. Dies ist erst der Fall, wenn der Text komplett aufgetaucht ist. In diesem Fall muss die Ausgabe in der Statusleiste zum Füllen Leerzeichen enthalten.
467
Navigationshilfen
if (pos == 0) { window.status = leer + ausgabe; leer += " "; ■
Die Leerzeichen werden so lange erhöht, bis 149 Stück eingefügt wurden. Dieses Auffüllen mit Leerzeichen bei jedem Funktionsaufruf erzeugt die Bewegung von links nach rechts. if (leer.length >= 150) { pos = text.length; leer = ""; }
■
Der zweite Fall tritt dann ein, wenn der Text auf der linken Seite noch nicht komplett sichtbar ist. Dies ist beispielsweise zu Beginn der Animation der Fall. Hier wird nur der Text ohne Leerzeichen ausgegeben und anschließend die Startposition um 1 nach links verschoben, bis sie 0 erreicht und der erste Fall eintritt. } else { window.status = ausgabe; pos--; }
■
Damit diese Animation läuft, ruft setTimeout() die Funktion nach 80 Millisekunden erneut auf. id = setTimeout("laufschrift()", 80); }
Die Funktion zum Anhalten der Laufschrift, wenn der Nutzer über den Link fährt, gleicht der aus Listing 16.9 und ist dort erklärt. Im Folgenden sehen Sie den vollständigen Code: Listing 16.11: Eine etwas komplexere Variante einer Laufschrift (status_laufschrift_komplex.html) Laufschrift in der Statusleiste = 150) { pos = text.length; leer = ""; } } else { window.status = ausgabe;
468
Statusleiste
pos--; } id = setTimeout("laufschrift()", 80); } function anhalten(linktext) { clearTimeout(id); window.status = linktext; return true; } //--> Markt+Technik
14 15 16 Abbildung 16.13: Der Text läuft nur einmal und wesentlich länger durch.
17 18 19 20 21
Diese Laufschrift mit Leerzeichen dazwischen lässt sich natürlich auch von rechts nach links realisieren. Allerdings ist dies ein wenig schwieriger, da die Leerzeichen nicht subtrahiert werden können. Stattdessen muss die Zahl der Leerzeichen mit einer Schleife und einer Zählervariablen realisiert werden. Sie finden dieses Beispiel im Download-Archiv unter dem Namen status_ laufschrift_komplex_andersherum.html im Ordner code\kap16.
22 WWW
23 24
16.4.3 Tipps & Tricks Es muss nicht immer die Standardlaufschrift sein. Lassen Sie beispielsweise zufällig Buchstaben auftauchen, bis am Schluss alle Buchstaben des Textes sichtbar sind. So eine Animation ist einfach realisierbar, aber noch nicht auf jeder x-beliebigen Webseite zu finden.
469
Navigationshilfen
Das folgende Beispiel verwendet zwei Grundzüge der Animation: ■
■
Eine Zufallszahl (Variable z) – Mit ihr wird festgestellt, ob ein Buchstabe angezeigt werden soll. Die Zufallszahl von JavaScript, die Math.random() liefert, liegt zwischen 0 und 1. Sie müssen sie also nur mit Math.round() runden und schon haben Sie 0 oder 1. Bei 0 wird in den String dann ein Leerzeichen eingefügt und bei 1 der Buchstabe selbst. Die Verzögerung (Variable verz) – Die Funktion mit dem zufälligen Einund Ausblenden der Buchstaben wird mittels setTimeout() immer wieder aufgerufen. Allerdings erhöht sich bei jedem Durchlauf die Verzögerung um 10 Millisekunden, sodass die Animation immer langsamer wird. Ist die Verzögerung dann größer gleich 300 Millisekunden, wird die Animation abgebrochen und (clearTimeout()) sowie der vollständige Text in der Statusleiste angezeigt.
Hier sehen Sie den kompletten Code: Listing 16.12: Eine einfache Textanimation (status_textanimation.html) Textanimation in der Statusleiste = 300) { clearTimeout(id); window.status = text; } } //-->
Eine einfache Textanimation
470
Laufschrift
Abbildung 16.14: Die Textanimation wird immer langsamer …
14 15 16 Abbildung 16.15: … und kommt zum Schluss zum Stillstand.
17 18 19 20 21
Textanimationen und Ähnliches können Sie natürlich auch in einem Textfeld eines Formulars ausgeben (siehe folgenden Abschnitt). Aus UsabilitySicht ist eine Animation immer bedenklich, da sie auf einer Website sehr viel Aufmerksamkeit auf sich zieht.
22
TIPP
23
16.5 Laufschrift Wundern Sie sich nicht, diese Überschrift heißt genauso wie Überschrift 16.4.2. Dort wurde die Laufschrift allerdings nur in der Statusleiste verwendet. Nun zeigen wir Ihnen, wie Sie die Laufschrift auch in einem Textfeld ausgeben können. Wenn Sie das Textfeld per CSS formatieren, können Sie beispielsweise auch den Rahmen ausblenden und so vor dem Nutzer verbergen, dass die Animation in einem Formularfeld stattfindet. Alternativ haben Sie natürlich auch die Möglichkeit, die Animationstechnik beispielsweise in einem -Element mit innerHTML auszugeben.
24
TIPP
471
Navigationshilfen
Laufschrift in einem Textfeld hat gegenüber der Statusleiste zwei Vorteile: ■ ■
Die Angaben zur URL in der Statusleiste werden nicht überschrieben. Die Statusleiste bleibt frei. Sie können für die Laufschrift eine nichtproportionale Schrift8 wie beispielsweise Courier verwenden.
Die Änderungen gegenüber der Laufschrift in der Statusleiste halten sich sehr in Grenzen. Als Grundlage dient hier Listing 16.11. Folgende Anpassungen müssen Sie vornehmen: ■
Zuerst benötigen Sie ein Formular mit Formularfeld. Im style-Attribut können Sie eine diktengleiche Schrift wie Courier angeben.
■
Statt mit window.status in der Statusleiste geben Sie die Laufschrift als Wert des Formularfelds aus. Ein Formularfeld wird browserübergreifend am besten mit document.Formularname.Feldname.value angesprochen: document.formular.feld.value = leer + ausgabe;
Mehr zu Formularen erfahren Sie in Kapitel 20 »Formulare«. REF
■
Entsprechend der Feldgröße (size-Attribut) sollte die Zahl der maximal möglichen Leerzeichen begrenzt werden. if (leer.length >= 60) { pos = text.length; leer = ""; }
■
Bei Formularfeldern sollte die Laufschrift nicht ganz so schnell ablaufen. Setzen Sie daher den Zeitwert für setTimeout() ein wenig nach oben. id = setTimeout("laufschrift()", 120);
Hier das Listing: Listing 16.13: Laufschrift in einem Textfeld (formular_laufschrift.html) Laufschrift im Textfeld = 60) { pos = text.length; leer = ""; } } else { document.formular.feld.value = ausgabe; pos--; } id = setTimeout("laufschrift()", 120); } //-->
14 15 16 17 18 Abbildung 16.16: Auch Uraltbrowser wie hier Netscape 2 kommen mit der Laufschrift zurecht.
19 20 21 22
Mit dem -Tag können Sie auch mit HTML eine Laufschrift realisieren. Allerdings ist dieses Tag auf den Internet Explorer, den Netscape Navigator 7 und den Mozilla9 beschränkt. Die übrigen Browser stellen nur den Text dar. Konqueror 3 ignoriert den Text sogar.
23 24
Listing 16.14: Eine Laufschrift mit HTML (marquee.html) --Kursfeuerwerk des DAX-- --Wachstumsmarkt JavaScript-
Das -Tag wurde im Konqueror erst in Version 3.3 eingeführt. Im Safari entspricht das Version 1.2.
9
Mozilla kann mit einigen Attributen nichts anfangen.
473
Navigationshilfen
Tabelle 16.9:
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
16.5.1 Laufschrift schützen Ein Textfeld mit Laufschrift sollte im Allgemeinen schreibgeschützt sein. Dafür rufen Sie einfach im onfocus-Ereignis für das Textfeld (Referenz mit this) die Methode blur() auf.
Der Fokus bezeichnet das zurzeit aktive Formularfeld. Die Methode blur() entfernt den Fokus. Sobald also das Textfeld aktiv ist, verliert es diesen Status sofort wieder, bevor der Nutzer etwas ausführen kann. Für neuere Browser können Sie alternativ das HTML-Attribut disabled verwenden. Allerdings wird damit auch meist das Textfeld ein wenig anders – meist aber mit grauer Schrift – dargestellt.10 Wenn Sie dies nicht stört, können Sie auch beide Möglichkeiten gemeinsam verwenden. Ein Beispiel hierzu finden Sie im Download-Archiv unter dem Namen formular_laufschrift_gesperrt.html im Verzeichnis code\kap16. WWW
Abbildung 16.17: Laufschrift mit der Option disabled
Netscape 2 und 3 sowie Internet Explorer 3 in beiden Versionen unterstützen blur() ebenfalls. Tabelle 16.10: blur()-Methode und HTML-Attribut disabled im Vergleich
blur() disabled
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
10 Opera 6 und Mozilla verwenden neben der grauen Schrift zusätzlich einen dunkelgrauen Hintergrund, was für Laufschriften nicht sehr gut aussieht.
474
Drucken mit JavaScript
16.6 Drucken mit JavaScript Für das Drucken mit JavaScript gibt es mit print() eine eigene Methode des window-Objekts. Sie ruft den Druckerdialog des Browsers auf. Ein automatischer Start des Druckers ist aus Sicherheitsgründen nicht möglich. Die Druckfunktion wird häufig bei Druckversionen einer Website verwendet.
print()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 16.11: Die Methode print() des window-Objekts
Da nicht nur die alten Browser Netscape 2 und 3 und Internet Explorer 3 (beide Versionen), sondern auch der Internet Explorer 4 print() nicht unterstützen, sollten Sie entweder eine Browserunterscheidung verwenden oder prüfen, ob das Objekt vorhanden ist (siehe Kapitel 14 »Browserunterscheidung«):
14 15 16 17
if (window.print) { window.print(); }
18
Im folgenden Skript findet die Methode in der Praxis Anwendung. Ein Link wird mit Druckfunktion belegt:
19
Listing 16.15: Drucken mit JavaScript (print.html) Drucken Drucken
20 21 22 23
In einer Seite mit Frames kann der Druckbefehl für jeden Frame einzeln gegeben werden.11 Um alle Frames zu drucken, können Sie im Frameset den Druckbefehl aufrufen. Unter Windows scheitert allerdings der Netscape Navigator 4.x an dieser Aufgabe. Hier müssen Sie alle Frames einzeln ansprechen – am besten mit der frames[]-Kollektion – und drucken. Der Internet Explorer besitzt zwei eigene Ereignisse für das Drucken: onbeforeprint vor dem Druck und onafterprint nach dem Druck. Im Internet Explorer 4 unter Windows können Sie auch über ActiveX-Objekte drucken.
24
INFO
11 Die Grundlagen zu Frames finden Sie in Kapitel 19 »Frames«.
475
Inhalt
17 Fenster 14
15
In manchen Bereichen des Internets springen dem ahnungslosen Nutzer beim Aufrufen – zugegebenermaßen meist zweifelhafter – Webseiten dutzende von neuen Browserfenstern entgegen. Die Begeisterung über diesen Angriff ist natürlich meistens begrenzt. Die sich öffnenden Fenster – auch Popups genannt – sind aber nicht nur auf unseriösen Seiten zu finden, sondern werden auch für Navigationsleisten, Sitemaps oder seriöse Werbemeldungen verwendet.
16
17
Dieses Kapitel erklärt die Hintergründe von Popups, zeigt zuerst, welche Dialogfelder mit JavaScript aufrufbar sind, und bietet zuletzt einige besondere Funktionen, beispielsweise wie sich der Fensterinhalt scrollen und das Fenster selbst – allerdings nur in manchen Browsern – bewegen lässt. Die hier verwendeten Methoden und Eigenschaften gehören alle zum windowObjekt. Das ist natürlich logisch, da es sich als Oberobjekt aller Browserobjekte um das Browserfenster kümmert.
17.1
18
19 INFO
20
Dialogfelder
21
Dialogfelder oder auch modale Fenster sind kleine Fenster, bei denen der Nutzer in irgendeiner Form reagieren muss. Er tritt mit dem Browser und damit mit der Webseite in einen Dialog.
22
17.1.1
23
Warnmeldung
Das einfachste Dialogfenster, das in diesem Buch oft zum Einsatz kam, ist die Warnmeldung: Listing 17.1:
24
alert() (alert.html)
window.alert("Meldung");
477
ndex
Diese Zeile erzeugt eine Warnmeldung mit dem Text »Meldung«. Der Nutzer kann dies nur bestätigen. Die Meldung selbst ist zwar normalerweise ein String, kann aber auch ein beliebiger anderer Datentyp sein.
Fenster
Abbildung 17.1: Die Warnmeldung
alert() gibt es bereits in Uraltbrowsern wie Netscape 2 und 3 und Internet Explorer 3 in beiden Versionen. Tabelle 17.1: alert() alert() Abbildung 17.2: Uraltbrowser wie hier Netscape 2 fügen leider noch ein, dass es sich um einen JavaScript-Aufruf handelt. Neuere Netscape-Browser und Mozilla schreiben das in die Titelleiste.
TIPP
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
In der Praxis wird diese Meldung hauptsächlich verwendet, um den Nutzer auf einen Fehler, beispielsweise ein falsch ausgefülltes Formularelement, aufmerksam zu machen. In der Entwicklungsphase eignet sich die Warnmeldung hervorragend, um zwischendurch Werte von Variablen zu testen und auszugeben.1 Vorsicht ist allerdings bei der Verwendung in Schleifen geboten. Ist die Schleife endlos und Sie haben darin eine Warnmeldung, hilft es unter Umständen nur noch, den Browser gewaltsam zu beenden.
17.1.2 Bestätigung Ein Dialogfeld mit window.confirm("Frage") erlaubt dem Nutzer bereits mehr Interaktion als alert(). Sie stellen in der Meldung eine Frage, die der Nutzer dann mit OK bestätigen oder mit ABBRECHEN verneinen kann. JavaScript liefert bei OK das Ergebnis true, bei ABBRECHEN false. Wenn das Bestätigungsdialogfeld erscheint, wird das Skript angehalten und wartet auf die Nutzereingabe.
1
478
Vor allem, da alert() alle Datentypen ausgeben kann!
Dialogfelder
Listing 17.2:
confirm() (confirm.html)
var erg = window.confirm("Haben Sie einen Führerschein?"); if (erg) { window.alert("Ein Mietwagen ist reserviert."); } else { window.alert("Sie können leider keinen Wagen mieten!"); }
14
Listing 17.2 fragt den Nutzer, ob er einen Führerschein hat. Je nach Antwort erhält er eine Meldung. Abbildung 17.3: Klickt der Nutzer an, dass er einen Führerschein hat, …
15 16 17
Abbildung 17.4: … wird ihm bestätigt, dass er einen Mietwagen erhält.
18 19 20
NS4.x
confirm()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 17.2: confirm()
21 22
In der Praxis wird confirm() hauptsächlich zum Bestätigen von Bedingungen verwendet, die der Website-Betreiber nicht selbst prüfen muss. Ein Beispiel ist die bekannte Frage, ob die AGB gelesen wurden. Allerdings bleibt hier das Problem, dass der Nutzer JavaScript deaktiviert haben könnte. Dann müssen Sie eine Alternative bieten.
23 24
17.1.3 Eingabefenster Das dritte browserübergreifende Dialogfeld ist das Eingabefenster. window.prompt("Beschreibung", Voreinstellung)
Dieses Fenster bittet den Nutzer in einem Beschreibungstext, etwas in das Textfeld einzugeben. Die Voreinstellung zeigt bereits einen Wert im Textfeld an, kann aber auch weggelassen werden. Allerdings erscheint dann in manchen Browsern undefined als Vorausfüllung. Verwenden Sie also besser einen leeren String, wenn Sie das Feld freilassen möchten. 479
Fenster
Tabelle 17.3: prompt() prompt()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel fragt den Nutzer in zwei Eingabefeldern nach zwei Zahlen und addiert diese anschließend: Listing 17.3:
prompt() (prompt.html)
var x = window.prompt("Erste Zahl?", ""); var y = window.prompt("Zweite Zahl?", ""); var erg = parseFloat(x) + parseFloat(y); alert(erg);
Abbildung 17.5: Geben Sie eine Zahl ein.
Abbildung 17.6: Das Ergebnis wird mit alert() ausgegeben.
HALT
Beachten Sie, dass der Rückgabewert von prompt()immer ein String ist. Benötigen Sie eine Zahl, sollten Sie den String in eine Zahl verwandeln, ansonsten kann es bei einer Addition wie im letzten Beispiel zu einer String-Konkatenation kommen. Wollten Sie dieses Beispiel in der Praxis einsetzen, müssten Sie zusätzlich eine Überprüfung einfügen, falls der Nutzer keine Zahl eingibt. In der Praxis kommt prompt() relativ selten zum Einsatz, da nur genau eine Eingabe abgefragt werden kann. Mit einem Formular – das eventuell noch in einem kleinen neuen Browserfenster geöffnet werden kann – haben Sie als Entwickler mehr Freiheiten.
480
Dialogfelder
17.1.4 Dialogfelder im Internet Explorer Der Internet Explorer bietet noch einige Besonderheiten, was Dialogfelder betrifft: ■
Die Methode window.showModalDialog("URL", Parameterarray, "Einstellungen") ruft ein Dialogfeld auf, das der Nutzer verlassen muss, bevor es im Browserfenster weitergeht (daher modal). Dieses Dialogfeld hat noch weitere Optionen. Als Parameter müssen Sie eine URL angeben. Die dort angegebene Seite ist der Inhalt des Dialogfelds. Optional können Sie mit einem Array Werte an das Dialogfeld übergeben. Diese Werte werden dann im Skript des Dialogfelds (in dem mit URL aufgerufenen Dokument) mit dialogArguments[] ausgelesen. Ebenfalls optional können Sie noch weitere Optionen für das Dialogfeld angeben. Hier die wichtigsten:
14 15 16
dialogWidth – Breite des Dialogfelds in Pixeln (px). ■ dialogHeight – Höhe des Dialogfelds in Pixeln (px). ■ dialogLeft – Position des Dialogs von links (auf dem Bildschirm relativ zur linken oberen Ecke des Dialogs). Angabe in Pixeln (px). ■ dialogTop – Position des Dialogs von oben (auf dem Bildschirm relativ zur linken oberen Ecke des Dialogs). Angabe in Pixeln (px). ■ center – richtet den Dialog mittig im Fenster aus, wenn der Wert yes oder 1 ist. Dies ist auch die Standardeinstellung. ■ resizable – gibt an, ob die Größe des Dialogs änderbar ist (yes oder 1) oder nicht (no oder 0). ■ scroll – legt fest, ob im Dialogfeld gescrollt werden darf (yes oder 1) oder nicht (no oder 0). ■ status – erlaubt dem Dialog eine Statusleiste (yes oder 1) oder lässt sie weg (no oder 0). Die Methode window.showModelessDialog("URL", Parameterarray, "Einstellungen") arbeitet genau wie showModalDialog(), allerdings muss das Dialogfeld nicht beendet werden, um ins Hauptfenster zurückzukehren. window.createPopup() erzeugt ein kleines Popup, das direkt über der Seite liegt. Es wird erst mit der Methode show(x, y, Breite, Höhe, Objekt)2 angezeigt und muss komplett per Skript gefüllt werden. Des Weiteren besitzt es keinen Fensterrahmen oder sonstige Fenstereigenschaften. Eine externe URL wie bei den anderen beiden Methoden ist also nicht möglich. ■
■
■
2
17 18 19 20 21 22 23 24
Das Objekt ist ein optionaler Parameter. Er gibt ein Objekt an, zu dem die x- und y-Koordinate in Relation stehen.
481
Fenster
Tabelle 17.4: Die besonderen Dialogfenster des Internet Explorers
NS4.x
showModalDialog() showModelessDialog() createPopup() a.
M/FF
IE4
IE5
IE5.5
IE6
IE7
()a
Op
SF/KQ
showModelessDialog() ist im Internet Explorer 5.x beim Mac nicht vorhanden.
showModalDialog() Das folgende Beispiel speichert die Namenseingabe des Nutzers (mit prompt()) in einem Array. Das Array wird bei showModalDialog() als Parameter-Array übergeben: Listing 17.4:
Ein Dialogfeld mit showModalDialog() (showModalDialog.html)
showModalDialog()
Die Datei inhalt.html, die im Dialogfeld dargestellt wird, greift über die Eigenschaft dialogArguments[] auf die Elemente des übergebenen Arrays zu: Listing 17.5:
Die Datei mit dem Inhalt des Dialogfelds (inhalt.html)
Dialogfeld
Hallo im Dialogfeld!
482
Dialogfelder
Abbildung 17.7: Der Nutzer gibt seinen Namen ein, …
14 Abbildung 17.8: … und das Dialogfeld kennt ihn.
15 16 17 18
Dieses Beispiel funktioniert mit showModelessDialog() analog. Sie finden das Beispiel im Download-Archiv unter showModelessDialog.html im Ordner code\kap17. Wenn Sie die hier vorgestellten Methoden im Web verwenden, müssen Sie natürlich mit einer Browserunterscheidung sicherstellen, dass nur der Internet Explorer den Dialogfeldaufruf erhält.
19
WWW
20 HALT
21
if (window.showModalDialog) { Aufruf; }
22
createPopup() Das folgende Beispiel für createPopup() lässt das Fenster erscheinen, wenn der Nutzer mit der Maus über einen Link fährt. Das Popup wird mit Stylesheet-Befehlen gefüllt, die von JavaScript aufgerufen werden.3 Zum Schluss zeigt die Methode show() das Popup an.
23 24
Verlässt der Nutzer den Link mit der Maus, wird das Popup mit hide() wieder ausgeblendet; es ist aber immer noch vorhanden. Listing 17.6:
Ein Popup für neuere Internet Explorer (createPopup.html) 4
createPopup() 3 4
Mehr zu Stil-Anweisungen in JavaScript erfahren Sie in Kapitel 22 »CSS und JavaScript«. Dieses Beispiel besitzt der Einfachheit halber keine Browserunterscheidung, die bei einer proprietären Funktion wie dieser in der Praxis noch ergänzt werden muss.
483
Fenster
Hauser und Wenz
Abbildung 17.9: Wenn der Nutzer über den Link fährt, erscheint das Popup.
17.1.5 Modale Fenster im Web 2.0 Neben den standardmäßig in JavaScript vorhandenen modalen Fenstern gibt es als Alternative zu Popups im Web 2.0 immer häufiger selbstgestaltete modale Fenster. Sie bestehen aus einem ausgegrauten oder halbtransparenten Hintergrund und davor einem Inhaltsbereich. Um ein solches Fenster zu realisieren, gibt es einen grundlegenden Ansatz: Das wichtigste Element ist ein -Block, der im Hintergrund liegt und den Rest der Seite überdeckt, und ein zweiter Block mit dem Inhalt des modalen Fensters.
484
Dialogfelder
Abbildung 17.10: Ein modales Fenster bei Pageflakes
14 15 16 17 18 19 20 Inhalt des modalen Fensters Schließen
21 22
Sie können die -Elemente selbstverständlich auch dynamisch per JavaScript erstellen, statt sie schon im HTML-Code vorzuhalten. Die Hauptfunktionalität übernimmt CSS. Damit werden das Fenster und der Inhaltsbereich formatiert. Der äußere -Block wird auf den Koordinaten 0, 0 positioniert und auf 100 % Breite skaliert. Damit im Internet Explorer auf der rechten Seite kein Rand erscheint, müssen im CSS vorher noch für das -Tag die Abstände auf 0 geschaltet werden. Für den Hintergrund wird ein halbtransparentes 1 *1 Pixel großes PNG verwendet. Damit der Internet Explorer bis Version 6 das auch schafft, setzen Sie noch einen Filter ein:
23 24
#modal1 { position: absolute; top: 0px; left: 0px; z-index: 100; width: 100%;
485
Fenster
height: 100%; background-image: url(transparent.png); filter:Alpha(opacity=50); }
Für den Inhaltsbereich wählen Sie eine mittige Position und weiße Hintergrundfarbe. #inhalt { position: absolute; top: 40%; left: 40%; width: 20%; height: 20%; background-color: white; padding: 5px; }
Nach den Vorbereitungen folgt der JavaScript-Teil. Hier wird das modale Fenster ein- und ausgeblendet. Nach der Browserobjekt-Prüfung folgt der Zugriff auf die Stil-Eigenschaft display. Sie wird überprüft und je nach Wert wird das modale Fenster ein- oder ausgeblendet. So kann dieselbe JavaScript-Funktion für das Ein- und Ausblenden verwendet werden: function modal(fenster) { if (document.getElementById) { if (document.getElementById(fenster).style.display == "none") { document.getElementById(fenster).style.display = "block"; } else { document.getElementById(fenster).style.display = "none"; } }
Hier der vollständige Code: Listing 17.7:
Modales Web 2.0-Fenster per JavaScript (modal_web20.html)
Modales Fenster body { margin: 0px;
486
Dialogfelder
padding: 0px; } #modal1 { position: absolute; top: 0px; left: 0px; z-index: 100; width: 100%; height: 100%; background-image: url(transparent.png); filter:Alpha(opacity=50); } #inhalt { position: absolute; top: 40%; left: 40%; width: 20%; height: 20%; background-color: white; padding: 5px; } Modales Fenster aufrufen Inhalt des modalen Fensters Schließen
14 15 16 17 18 19 20 21 Abbildung 17.11: Das Fenster lässt sich auf Knopfdruck aufrufen ...
22 23 24
487
Fenster
Abbildung 17.12: … und wieder schließen.
17.2
Popups und andere neue Fenster
Ein neues Fenster oder Popup öffnen Sie in JavaScript mit der Methode open(). Diese Methode und ihre Möglichkeiten verdienen eine ausführlichere Beschreibung. open() hat folgende Parameter: window.open("URL", "Name", "Einstellungen", Ersetzen);
Die Parameter sind alle optional. Werden sie alle weggelassen, öffnet der Browser ein leeres Fenster ohne Namen in den Standardeinstellungen des Browserbefehls für ein neues Fenster.5 ■ ■
URL – gibt die Adresse des Dokuments an, in der das Fenster geöffnet werden soll. Name – steht für den Namen des Fensters. Dies entspricht der Angabe eines Namens im HTML-Attribut target. Für den Namen gibt es noch einige weitere Optionen, die den Werten des target-Attributs in HTML entsprechen: ■ ■
■
5
488
_blank – ein neues Fenster ohne Namen _parent – das Dokument wird im übergeordneten Frameset geöffnet. _self – das Dokument wird im aktuellen Fenster oder Frame geöffnet.
Internet Explorer: DATEI/NEU/FENSTER bzw. im IE 7 SEITE/NEUES FENSTER und im Firefox DATEI/NEUES FENSTER.
Popups und andere neue Fenster
_top – das Dokument wird im aktuellen Fenster auf oberster Ebene geöffnet. Eventuell vorhandene Framesets verschwinden. ■ _media – das Dokument oder der Link wird in der Explorer-Leiste MEDIEN des Internet Explorers abgelegt. Dies funktioniert nur im Internet Explorer 6 und 7 und ist hauptsächlich für Musik und Videos sinnvoll. ■ _search – das Dokument oder der Link wird in der Explorer-Leiste SUCHEN abgelegt. Auch dies funktioniert nur im Internet Explorer, allerdings bereits ab Version 5. Einstellungen – enthält Konfigurationsmöglichkeiten für das neue Browserfenster. Sie finden diese in Abschnitt 17.2.2 »Einstellungen«. Ersetzen – enthält einen Boolean, der angibt, ob das neue Fenster das aufrufende Dokument in der History ersetzt (true) oder nicht (false, Standardeinstellung). ■
■ ■
14 15 16 17
window.open() sollte nicht in der Kurzform open() geschrieben werden, da auch document eine open()-Methode besitzt. HALT
18
Das folgende Beispiel öffnet eine Seite in einem neuen Fenster. Zusätzliche Einstellungen werden nicht vorgenommen: Listing 17.8:
19
Ein Fenster öffnen (open.html)
open() Hauser und Wenz
20 21 22 23
open() gibt es seit Netscape 2 und Internet Explorer 3 in der ersten Version. NS4.x
window.open()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 17.5: window.open()
489
24
Fenster
Abbildung 17.13: Die neue Webseite wurde in einem neuen Fenster geöffnet.
17.2.1 Popup-Blocker Ein Problem beim Öffnen neuer Fenster mit JavaScript liegt in den PopupBlockern, die mittlerweile auch in den Standardbrowsern integriert sind. Alle Popup-Blocker blockieren Popups, die beim Laden oder anderen Ereignissen ausgeführt werden, die nicht vom Nutzer, sondern z.B. durch setTimeout oder onload angestoßen werden. Sich auf onclick öffnende Popups werden standardmäßig nicht geblockt, lassen sich aber auch blockieren. Um einen Popup-Blocker festzustellen, können Sie eine Funktion vorschalten, die das Popup bereits öffnet: Listing 17.9:
Popup-Prüfung wird vorgeschaltet (popupcheck.html)
createPopup() Hauser und Wenz
14 15 16
In der geöffneten Seite ändern Sie den Wert einer Variablen aus der aufrufenden Seite. Der Zugriff auf die aufrufende Seite erfolgt per window.opener:6
17
Listing 17.10: Die geöffnete Seite liefert true zurück, wenn sie geöffnet werden kann
(popupcheck2.html). createPopup()
Kurze Wartezeit notwendig.
18 19 20 21
Die entscheidende Frage ist trotz möglichem Popup-Blocker-Test: Wie gehen Sie als Webentwickler mit geblockten Popups um? Hierauf gibt es zwei Antworten: Sie sollten nicht komplett auf teilweise sinnvolle Popups verzichten. Allerdings müssen Sie auf der anderen Seite Alternativlösungen bieten. Öffnet sich ein Fenster nicht, sollte der Nutzer also nicht im Regen stehen. Popups, die sich beim Anklicken öffnen, wie beispielsweise eine Sitemap, sind heute noch weitgehend sicher, automatisch geladene Popups dagegen erzeugen beim Nutzer häufig bereits zwangsläufig ein merkwürdiges Gefühl, da sie mittlerweile als unseriös angesehen oder mit Werbung assoziiert werden. Eine weitere Alternative zu Popups sind per JavaScript erzeugte eingeblendete Fenster, wie sie in Web 2.0-Anwendungen oft eingesetzt werden.
6
22 23 24
Sie finden im Abschnitt 17.2.6 »opener« ein paar Details zu window.opener.
491
Fenster
17.2.2 Einstellungen Bis jetzt war das Öffnen des neuen Fensters noch keine Kunst und hätte auch über das target-Attribut in HTML bewerkstelligt werden können. Erst die möglichen Einstellungen machen es richtig interessant. Die Einstellungen werden durch Kommata getrennt hintereinander angegeben.7 Eigenschaft und Wert sind durch ein Ist-Gleich (=) verbunden: window.open("http://www.hauser-wenz.de", "Fenster1", "width=750,height=500");
Diese Zeilen erzeugen ein neues Browserfenster mit einer Breite von 750 Pixeln und einer Höhe von 500 Pixeln. Abbildung 17.14: Die Homepage öffnet sich in einem Fenster mit festgelegter Größe.
Viele Einstellungen erwarten Wahrheitswerte als Angabe. Wahrheitswerte werden hier allerdings nicht mit true oder false angegeben, sondern mit yes und no. window.open("http://www.hauser-wenz.de", "Fenster1", "width=750,height=500,resizable=no");
Mit dieser Zeile wird ein Browserfenster erzeugt, das nicht in der Größe veränderbar ist. Alternativ zu yes kann auch die 1 und alternativ zu no die 0 verwendet werden. Wenn Sie den Wert weglassen, wird automatisch von yes ausgegangen. Folgende Zeile verhindert also auch, dass das Browserfenster eine variable Größe hat: window.open("http://www.hauser-wenz.de", "Fenster1", "width=750,height=500,resizable");
7
492
Sie sollten nach den Kommata keine Leerzeichen einfügen, da Netscape 4.x damit Probleme hat.
Popups und andere neue Fenster
Sobald auch nur eine der Einstellungen in open() vorgenommen wird, werden alle anderen Einstellungen automatisch auf no geschaltet. Dies sehen Sie auch an dem Beispiel in Abbildung 17.14.
INFO
Die folgende Tabelle 17.6 zeigt alle möglichen Einstellungen. Bei Wahrheitswerten wird nur die Bedeutung von yes erklärt, no ist das Gegenteil. Die Kurzformen dürfen ebenfalls verwendet werden. Tabelle 17.7 gibt für die Einstellungen an, mit welchem Browser sie jeweils kompatibel sind. Einstellung
Beschreibung
alwaysLowered
Neues Fenster steht immer hinter anderen Browserfenstern (yes). Funktioniert nur mit einem signierten Skript im Netscape (siehe Kapitel 30 »Sicherheit«).
14 Tabelle 17.6: Einstellungen für Fenster
15 16
alwaysRaised
Neues Fenster steht immer vor anderen Browserfenstern (yes). Funktioniert nur mit einem signierten Skript im Netscape.
channelmode
Stellt den Internet Explorer ab Version 4 bei yes im Channel-Modus dar, das heißt, Adressleiste und Menüleiste verschwinden und der Browser ist in der maximierten Ansicht. Standardeinstellung ist immer no.
dependent
Macht bei yes das neu geöffnete Fenster vom vorigen abhängig. Wird dies geschlossen, schließt sich auch das neu geöffnete.
directories
Blendet bei yes die Symbolleiste mit Links und Ähnlichem ein. Im Internet Explorer heißt sie beispielsweise LINKS, im Netscape Navigator und Mozilla PERSÖNLICHE SYMBOLLEISTE bzw. PERSONAL TOOLBAR. Ist im Netscape Navigator ab Version 6 die Symbolleiste im Originalfenster ausgeblendet, wird sie auch nicht im neuen Fenster angezeigt.
fullscreen
Zeigt bei yes im Internet Explorer ab Version 4 das neue Fenster in der maximierten Ansicht über den gesamten Bildschirm an.
height
Gibt die Höhe des neuen Fensters in Pixeln an. Dazu darf allerdings keine Angabe der Einheit erfolgen.
hotkeys
Deaktiviert bei no die meisten Tastenkürzel (außer beispielsweise zum Schließen des Browsers) für ein neues Fenster. Dies funktioniert allerdings nur bei signierten Skripten im Netscape Navigator.
22
innerHeight
Legt die innere Höhe, also die Höhe des Dokumentfensters, im Netscape Navigator und Mozilla in Pixeln fest. Wird die Größe zu klein, benötigen Sie dazu ein signiertes Skript.
23
innerWidth
Legt die innere Breite, also die Breite des Dokumentfensters, im Netscape Navigator und Mozilla in Pixeln fest. Wird die Größe zu klein, benötigen Sie dazu ein signiertes Skript.
left
Gibt die x-Koordinate in Pixeln von der linken oberen Ecke des Bildschirms aus an und ist auf den Internet Explorer ab Version 4 beschränkt.
location
yes blendet die Adressleiste des Browsers mit der URL ein.
menubar
yes zeigt die Menüleiste des Browsers an.
outerHeight
Legt die äußere Höhe, also die Höhe des Browserfensters, im Netscape Navigator und Mozilla in Pixeln fest. Wird die Größe zu klein, benötigen Sie dazu ein signiertes Skript.
17 18 19 20 21
24
493
Fenster
Tabelle 17.6: Einstellungen für Fenster (Forts.)
Einstellung
Beschreibung
outerWidth
Legt die äußere Breite, also die Breite des Browserfensters, im Netscape Navigator und Mozilla in Pixeln fest. Wird die Größe zu klein, benötigen Sie dazu ein signiertes Skript.
resizable
Gibt an, ob das Browserfenster vom Nutzer in der Größe geändert werden darf (yes) oder nicht (no).
screenX
Gibt die x-Koordinate in Pixeln von der linken oberen Ecke des Bildschirms aus an und ist auf Netscape ab Version 4 und Mozilla beschränkt.
screenY
Gibt die y-Koordinate in Pixeln von der linken oberen Ecke des Bildschirms aus an und ist auf Netscape ab Version 4 und Mozilla beschränkt.
scrollbars
yes zeigt die Scrollbalken im Browser an.
status
yes zeigt die Statusleiste im Browser an.
titlebar
no blendet die Titelleiste des Browsers aus. Funktioniert nur mit einem signierten Skript in Netscape ab Version 4. Im Internet Explorer muss es sich um eine HTML Applicationa (HTA) oder um ein sicheres Dialogfeld handeln. Mehr dazu erfahren Sie in Kapitel 30 »Sicherheit«.
toolbar
Blendet bei yes die Werkzeugleiste mit Vor, Zurück, Neuladen usw. an. Im Internet Explorer heißt sie STANDARDSCHALTFLÄCHEN, im Netscape Navigator NAVIGATIONS-SYMBOLLEISTE.
top
Gibt die y-Koordinate in Pixeln von der linken oberen Ecke des Bildschirms aus an und ist auf den Internet Explorer ab Version 4 beschränkt.
width
Gibt die Breite des neuen Fensters in Pixeln an, ohne dass die Einheit angegeben sein darf.
z-lock
Bei yes wird das neu geöffnete Browserfenster nicht über anderen Fenstern angezeigt, wenn es den Fokus erhält.
a.
WWW
Tabelle 17.7: Browserkompatibilität der Einstellungen
Quelle zu HTA: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/hta/ overview/htaoverview.asp.
Im Download-Archiv finden Sie zu den Einstellungen, die nicht in anderen Beispielen in diesem Kapitel verwendet werden, jeweils ein kurzes Exempel. Die Dateien sind mit den Namen der Einstellungen bezeichnet, beispielsweise toolbar.html für die Einstellung toolbar, und befinden sich im Ordner code\kap17. NS4.x
M/FF
alwaysLowered
alwaysRaised
channelmode
494
dependent
directories
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Popups und andere neue Fenster
NS4.x
M/FF
fullscreen
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
height
hotkeys
innerHeight
innerWidth
left
location
menubar
outerHeight
outerWidth
resizable
screenX
screenY
scrollbars
status
14
titlebar
toolbar
z-lock
16 17
width
15
top
Tabelle 17.7: Browserkompatibilität der Einstellungen (Forts.)
18 19 20 21
22 23
Die verschiedenen browserspezifischen Einstellungen behindern sich nicht gegenseitig, da die Browser die Einstellungen ignorieren, die sie nicht unterstützen. So geben Sie die Position des Fensters im Internet Explorer mit left und top an, im Netscape Navigator und Mozilla dagegen mit screenX und screenY. Der Opera kennt viele Einstellungen nicht, da das Fenster als Register gehandhabt wird.
24
INFO
Volle Kontrolle Mit einem signierten Skript erhalten Sie in Netscape bereits eine recht große Kontrolle über den Browser. Das folgende Beispiel erstellt ein neues Browserfenster, das sich immer im Vordergrund befindet (alwaysRaised), nicht in
495
Fenster
der Größe veränderbar (resizable) ist und ohne Titelleiste (titlebar) auskommt. Die Breite und Höhe werden nachträglich auf die Maße des Bildschirms eingestellt. Listing 17.11: Volle Kontrolle über den Netscape Navigator (kontrolle.html) open() Hauser und Wenz
Abbildung 17.15: Der Nutzer muss Zugriffsrechte vergeben.
REF
496
Unsignierte Skripte funktionieren nur dann, wenn Sie lokal darauf zugreifen oder eine Sicherheitseinstellung für den Browser ändern. Mehr dazu in Kapitel 30 »Sicherheit«.
Popups und andere neue Fenster
Abbildung 17.16: Die Webseite beherrscht den gesamten Bildschirm und hat nun keinen Rahmen mehr.
14 15 16 17 18 19
17.2.3 Fokus
20
Wenn Sie ein neues Fenster mit JavaScript aufrufen, erscheint es im Vordergrund, es erhält also den Fokus. Dieses Verhalten ist auch meist gewünscht. Rufen Sie dagegen aus einem Fenster das neue Fenster erneut auf und ein Fenster mit diesem Namen ist bereits geöffnet, erhält es nicht den Fokus. Das ist meist nicht gewünscht und insbesondere bei einer Sitemap oder einer Werbung sehr ärgerlich, da der Nutzer sie auch beim zweiten Laden noch sehen sollte.
21 22
In diesem Fall hilft die Methode focus() des window-Objekts. Sie gibt einem Fenster (oder Formularelement) den Fokus. In diesem Beispiel soll das neu aufgerufene Fenster beim Laden den Fokus erhalten. Dazu dient das onloadEreignis im -Tag:
23 24
onload="this.focus()"
this steht für das aktuelle Fenster.
Der Zugriff auf das Fenster kann jedoch auch aus dem aufrufenden Skript in Verbindung mit dem Fensternamen erfolgen. Dies ist beispielsweise bei einer externen URL sinnvoll.
497
Fenster
Listing 17.12: Den Fokus vergeben (focus.html) open() Hauser und Wenz
Das Gegenstück zu window.focus() ist blur(). Damit nehmen Sie einem Fenster den Fokus.
17.2.4 In Popups schreiben In einem neu geöffneten Fenster können Sie ein neues Dokument oder einen externen Link laden. Alternativ lässt sich das Fenster allerdings auch komplett mit per Skript erstelltem Inhalt füllen. Im folgenden Beispiel enthält ein Text einen Begriff, der in einem PopupFenster näher erläutert werden soll. Der Begriff wird also in einen Link gepackt. Sobald der Nutzer darauf klickt, soll sich ein Fenster mit einem Beschreibungstext öffnen. ■
In der Funktion oeffnen(), die beim Anklicken des Links aufgerufen wird, erhält zuerst die Variable fenster die Referenz auf das neu geöffnete Fenster. Die URL als erster Parameter erhält keine Angaben und es wird auch kein Fenstername vergeben. var fenster = window.open("", "", "width=300,height=200,resizable=no");
■
Eine Variable speichert nun den HTML-Quellcode für die neue Seite: var text = "Neues Fenster"; text += "Skriptsprache
Eine Scriptsprache führt "; text += "kleinere Programme, so genannte Skripten, aus.
"; text += "";
■
Die Variable wird mit document.write() in das neue Fenster geschrieben: fenster.document.write(text);
■
Der Optik halber ändert eine Stil-Anweisung auch die Hintergrundfarbe. Beachten Sie, dass sie in älteren Browsern – beispielsweise Netscape 4.x – nicht funktioniert. fenster.document.body.style.backgroundColor = "orange";
498
Popups und andere neue Fenster
■
Zum Schluss schließt die document.close()-Anweisung das Schreiben in das neue Fenster. Fehlt die Anweisung, zeigen der Internet Explorer 4 und die neueren Netscape-Browser sowie Mozilla ein Wartesymbol8 als Mauszeiger, da für sie das Schreiben noch nicht abgeschlossen ist. fenster.document.close();
Hier der vollständige Code:
14
Listing 17.13: In ein neues Fenster schreiben (popup_schreiben.html) open() JavaScript ist eine Scriptsprache.
15 16 17 18 19 20 Abbildung 17.17: Das neue Fenster wird mit den Inhalten geöffnet, die im alten Fenster definiert wurden.
21 22 23 24
8
Je nach Betriebssystem und Cursor-Satz beispielsweise eine Sanduhr.
499
Fenster
TIPP
Auf älteren Browsern (z.B. Internet Explorer 4) oder langsameren Systemen kann es zu einem Fehler kommen, wenn das Fenster zu langsam geöffnet und bereits in das Fenster geschrieben wird. Um dies zu verhindern, schreiben Sie erst nach einer mit setTimeout() produzierten Zeitverzögerung in das neue Fenster: open() JavaScript ist eine Scriptsprache.
Erst nach einer Zeitverzögerung das Dokument füllen (popup_schreiben_ timeout.html) Beachten Sie, dass die Referenz auf das Fenster – hier die Variable fenster – global sein muss, also auch in der Funktion zum Schreiben zur Verfügung stehen muss.
17.2.5 Fenster schließen Das Gegenstück zu window.open() bildet window.close(), um ein beliebiges Browserfenster wieder zu schließen. close() gibt es seit Netscape 2 und Internet Explorer 3 in der ersten Version. Tabelle 17.8: window. close()
500
NS4.x
window.close()
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Popups und andere neue Fenster
In einem einfachen Beispiel wird zuerst ein neues Fenster geöffnet. Listing 17.14: Ein neues Fenster öffnen (fenster_schliessen.html) var fenster = window.open("neu.html", "Fenster1", "width=300,height=200,resizable=no");
In der Datei neu.html für das neue Fenster befindet sich ein Link, der das neue Fenster schließt. Beachten Sie auch, dass das Fenster sich beim Laden automatisch selbst den Fokus gibt.
14
Listing 17.15: Das Fenster lässt sich direkt wieder schließen (neu.html).
15
Neues Fenster Fenster schließen
16 17 18 19
Alternativ können Sie das Fenster natürlich auch aus der aufrufenden Datei schließen. Für diesen Fall sollten Sie allerdings überprüfen, ob das Fenster bereits erstellt wurde oder aber bereits wieder geschlossen ist, da der Browser ansonsten einen Fehler liefert. Ob ein Fenster geschlossen ist, stellen Sie mit der Eigenschaft closed fest.
20
Listing 17.16: Ein neues Fenster im aufrufenden Fenster schließen (fenster_schliessen2.html)
21
open() Neues Fenster Neues Fenster wieder
22 23 24
501
Fenster
schließen
Abbildung 17.18: Das neue Fenster aus dem Ursprungsfenster schließen.
Tabelle 17.9: closed closed
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
17.2.6 opener In vielen Fällen ist es nicht nur interessant, das neue Fenster fernzusteuern, sondern auch, aus dem neuen Fenster auf das Ursprungsfenster zuzugreifen. Dazu dient die Eigenschaft opener des window-Objekts. Sie verweist auf das Fenster, das ein Fenster per JavaScript geöffnet hat. In der Praxis wird diese Eigenschaft vor allem für Sitemaps9 verwendet. Der Zugriff klappt allerdings nur, wenn sich die geladene Seite unter derselben Domain befindet. Netscape 2 unterstützt opener noch nicht, die späteren Browser sind jedoch damit vertraut.
9
502
Eine Sitemap zeigt die Struktur einer Webseite und verlinkt zu den wichtigsten Unterpunkten.
Popups und andere neue Fenster
opener
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 17.10: opener
Eine Sitemap sehen Sie im folgenden Beispiel. Das Beispiel besteht aus zwei Dateien: der Homepage (home.html), von der aus die Sitemap aufgerufen wird und der Sitemap selbst. Ist die Sitemap bereits geöffnet, kann sie über denselben Link wieder geschlossen werden.10 Dazu prüft eine Fallunterscheidung, ob das Fenster mit der Sitemap aktuell geöffnet ist.
14 15
Listing 17.17: Die Homepage (home.html) Homepage Sitemap
16 17 18 19 20 21
Die Sitemap selbst besteht aus mehreren Links. Die Links rufen jeweils im onclick-Ereignis eine Funktion auf. Diese Funktionen verwenden window.opener, um auf das Hauptfenster zu verweisen, und ändern dessen Adresse mit location.href.
22
Listing 17.18: Die Sitemap (site.html)
23
Neues Fenster
24
10 Dieses Verhalten ist natürlich Geschmacksache. Sie können durchaus auch einen zweiten Link zum Schließen anbieten oder nur einen Link in der Sitemap verwenden. Testen Sie dies am besten mit einigen – möglichst unerfahrenen – Internetnutzern.
503
Fenster
Hauptseite alert() prompt() Fenster schließen
Abbildung 17.19: Mit den Links in der Sitemap steuern Sie, was im Ursprungsfenster geschieht.
17.3
Scrollen
Das Scrollen von Fenstern, im Prinzip ein Bewegen des Fensterinhalts, lässt sich nicht nur mit Ankern, sondern auch mit JavaScript bewerkstelligen. Dazu stehen drei Methoden des window-Objekts zur Verfügung: ■
■
504
scroll(x, y) – scrollt zu den Koordinaten x und y. Wollen Sie nur horizontal oder vertikal scrollen, sollten Sie die andere Koordinate auf 0 setzen. scrollTo(x, y) – ersetzt scroll() in allen Browsern ab Netscape 4.x und Internet Explorer 4. Da damit nur der Navigator 3 ausgeschlossen wird, ist diese Methode sinnvoll einsetzbar.
Scrollen
■
scrollBy(x, y) – scrollt relativ von der aktuellen Scrollposition aus. Auch negative Werte sind möglich.
scroll() ist bereits im Netscape Navigator 3 enthalten. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
scrollTo()
scrollBy()
scroll()
Tabelle 17.11: Methoden zum Scrollen
15 16
Im folgenden Beispiel werden alle drei Methoden eingesetzt, um verschiedene Links mit Scrollfunktion zu versehen: Listing 17.19: Scrollen (scrollen.html)
17
Scrollen Scroll Scroll weiter Zurück zum Anfang
Der Aufruf zum Scrollen darf nicht im onclick-Ereignis eines Links stehen, da hier der Link ausgeführt würde. Dann wird, auch wenn der Link ins Leere läuft (Wert: #), die Scrollposition zurückgesetzt.
14
18 19 20 21 22
HALT
Abbildung 17.20: Ein Klick auf den Link, …
23 24
505
Fenster
Abbildung 17.21: … und der Browser scrollt.
INFO
Der Internet Explorer ab Version 4 unterstützt das Ereignis onscroll, das eintritt, wenn im Fenster gescrollt wird. Der Netscape Navigator ab Version 6 und Mozilla unterstützen die Methoden scrollByLines() und scrollBy Pages(), um anzugeben, wie viele Linien oder Seiten gescrollt werden sollen. Allerdings sind diese Maßeinheiten sehr schwer einschätzbar.
17.3.1 Scrollen verhindern Wenn Sie das Scrollen des Nutzers komplett unterdrücken möchten, ist das Ausblenden der Scrollleisten bei einem neu geöffneten Fenster nicht ausreichend. Sie müssen zusätzlich gelegentlich (setTimeout) die Scrollposition auf den Ursprung zurücksetzen (scrollTo(0, 0)), da mit einer Scrollmaus auch ohne Scrollleisten gescrollt werden kann. Verwenden Ihre Nutzer ausschließlich den Internet Explorer ab Version 4, können Sie dazu auch das Ereignis onscroll verwenden, da dies zeitsparender als der ständige Aufruf von setTimeout() ist.
17.4
Fenster bewegen und skalieren
Zum Skalieren und Bewegen von Fenstern gibt es vier Methoden des windowObjekts: ■ ■
■
506
moveTo(x, y) – bewegt das Fenster zu den festgelegten x- und y-Koordinaten. moveBy(x, y) – bewegt das Fenster um die dort angegebene Zahl an Pixeln. Ein positiver Wert bewegt nach rechts und unten, ein negativer nach links und oben. Die Änderung ist also relativ zur aktuellen Position. resizeTo(Breite, Höhe) – ändert die Größe des Fensters auf die angegebene Breite und Höhe.
Fenster bewegen und skalieren
■
resizeBy(Breite, Höhe) – ändert die Größe des Fensters, indem es die angegebene Breite und Höhe addiert (positiver Wert) oder subtrahiert (negativer Wert). Die Änderung ist also relativ zur aktuellen Größe.
In älteren Browsern sind die vier Methoden nicht vorhanden. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
moveTo()
moveBy()
resizeTo()
resizeBy
Tabelle 17.12: Methoden zum Bewegen und Skalieren
14 15 16
Das folgende Beispiel verwendet die Methoden resizeTo() und moveTo(), um ein neu geöffnetes Fenster beim Anklicken eines Links zu vergrößern beziehungsweise zu bewegen:
17 18
Listing 17.20: Das neue Fenster in der Größe verändern und bewegen
(fenster_skalieren_bewegen.html) Fenster skalieren und bewegen Neues Fenster Vergrößern Bewegen
19 20 21 22 23 24
507
Fenster
Abbildung 17.22: Das neue Fenster im Original
Abbildung 17.23: Das Fenster wird vergrößert …
508
Fenster bewegen und skalieren
Abbildung 17.24: … und nach unten bewegt.
14 15 16 17 18 19 Im Download-Archiv finden Sie ein Beispiel, das ein Fenster mit moveBy() und resizeBy() relativ bewegt und skaliert. Es trägt den Namen fenster_ relativ_skalieren_bewegen.html und befindet sich im Ordner code\kap17. Beim relativen Skalieren können Sie natürlich die Informationen des screenObjekts auslesen, um auf die aktuelle Position und Größe des Browserfensters zu reagieren.
20
WWW
21 TIPP
22 23 24
509
Inhalt
18 Cookies 14
15
Der Begriff Cookies ist älter als das World Wide Web. Bereits vor der Erfindung des HyperText-Prinzips durch Tim Berners-Lee und seine Arbeitsgruppe am CERN geisterten die Begriffe Cookie und MagicCookie durch die IT-Landschaft. Damals bezeichnete ein Cookie einen kleinen Datenblock, der meist zur Identifikation oder für Zugriffsrechte diente und durchaus auch im Hackerbereich gebräuchlich war. Mit dieser ursprünglichen Bedeutung haben Cookies im Web heute kaum mehr etwas zu tun. Dort ist ein Cookie eine kleine Textdatei bzw. ein Textfragment, das der Browser auf dem Rechner des Nutzers anlegt. Das heißt, der Browser hat die Kontrolle über die Datei. Der Inhalt kann allerdings mit einer Skriptsprache geändert werden.
16
17
18
Dazu ist es wichtig, zu verstehen, warum Cookies eingeführt wurden. Das Webprotokoll HTTP dient zur Übermittlung der notwendigen Daten einer Webseite. Es unterliegt allerdings einer Beschränkung: Es ist ein statusloses Protokoll. Statuslos bedeutet, das Protokoll hat kein Gedächtnis, weiß also nicht, ob es Daten bereits einmal geschickt hat, und es kann auch nicht zur Identifizierung des Nutzers herangezogen werden.
19
20
An dieser Stelle kommen Cookies ins Spiel. Mit einer Skriptsprache kann der Webprogrammierer Informationen in das Cookie schreiben, die dann bei jeder Kommunikation zwischen dem Browser und dem Server ausgetauscht werden. Diese Informationen müssen aus einer einzelnen Textzeile bestehen, da sie innerhalb des HTTP-Headers übermittelt werden. Diese Informationen stehen dem Programmierer nun auch auf anderen Webseiten (derselben Website) zur Verfügung.
21
22
23
Mit einer serverseitigen Skriptsprache funktioniert dies folgendermaßen: Der Browser schickt per HTTP die vorhandenen Cookies des Servers.1 Mit der serverseitigen Skriptsprache können Sie ein neues Cookie setzen oder bestehende ändern und löschen. 3. Die Änderungen werden per HTTP an den Browser geschickt.
1. 2.
24
JavaScript erlaubt als clientseitige Skriptsprache ebenfalls den Zugriff auf Cookies. So können Sie häufig anfallende Aufgaben im Web – wie einen Login oder den Transport von Werten über mehrere Seiten – auch mit JavaScript bewältigen. Der Server erhält nur Cookies, die er selbst gesetzt hat. Damit wird das Ausspionieren des Nutzers verhindert (siehe Abschnitt 18.1.2 »Sicherheit«).
511
ndex
1
Cookies
18.1
Geschichte
Bevor Sie die ersten Codezeilen in JavaScript zu sehen bekommen, finden Sie hier wichtige Details zur Geschichte, den Standards und insbesondere der Sicherheitsdiskussion rund um Cookies.2 Ins Leben gerufen wurden die Cookies von Netscape. Dementsprechend entstand die erste offizielle Dokumentation. Diese Spezifikation ist allerdings nie ein offizieller Standard geworden. Andere Browser haben sie vielmehr stillschweigend übernommen. Cookies werden also von allen relevanten Browsern unterstützt. Tabelle 18.1: Cookies und ihr JavaScript-Zugriff
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Die Cookiespezifikation hat mittlerweile die IETF (Internet Engineering Task Force) übernommen. Sie finden den offiziellen Standard unter http://www.ietf.org/rfc/rfc2965.txt.
18.1.1 Beschränkungen Die Spezifikationen führen zu einigen Beschränkungen für Cookies, die in der Praxis von den meisten Browsern eingehalten werden und die dafür sorgen, dass mit Cookies kein Missbrauch betrieben werden kann: ■ ■ ■ ■
■
2 3 4
512
Ein Cookie darf nur 4 Kbyte groß sein. Dies entspricht 4.096 Byte und – da ein Byte einem ASCII-Zeichen entspricht – auch 4.096 ASCII-Zeichen. Ein Browser lässt nicht mehr als 300 Cookies zu. Sobald mehr Cookies vorhanden sind, werden die ältesten automatisch gelöscht.3 Nur die Domain, die ein Cookie gesetzt hat, darf es auslesen. Daher darf www.spiegel.de keine Cookies von www.focus.de lesen.4 Eine Domain oder ein Webserver darf maximal 20 Cookies setzen. Auch hier gilt das First-In/First-Out-Prinzip. Die ältesten Cookies werden also zuerst gelöscht. Cookies können an den Pfad gebunden werden, aus dem sie gesetzt wurden. Ist kein Pfad gesetzt, ist das Cookie für alle Pfade der Domain verfügbar. Als Alternative kann ein Pfad angegeben werden, aus dem ein Cookie ausgelesen wird. Der Originalpfad darf das Cookie dann allerdings weder ändern noch auslesen. Ein Beispiel wäre ein Cookie, das von www.hauserwenz.de mit dem Pfad support gesetzt wurde. Es kann von www.hauserwenz.de/publikationen nicht ausgelesen werden. Ursprünglich angeheizt wurde diese Diskussion durch einen Artikel in einem amerikanischen Computermagazin, der Cookies verteufelt hat. Dies ist eine einfache Form des First-In/First-Out-Prinzips. Es gab eine Sicherheitslücke im Internet Explorer 5, die dies außer Kraft setzen konnte. Sie wurde jedoch schnell wieder geschlossen.
Geschichte
Da Cookies bei jeder Kommunikation über das Netzwerk ausgetauscht werden, ist es nicht sinnvoll, mehr Informationen als unbedingt notwendig in Cookies abzulegen.
TIPP
18.1.2 Sicherheit
14
Bei den genannten Beschränkungen stellt sich die Frage, welches Sicherheitsproblem eigentlich bei Cookies besteht. Dass Cookies Viren einschleusen könnten oder böse Dateien enthalten, wird dadurch ausgeschlossen, dass der Browser sie kontrolliert und es sich wirklich nur um reine Textdateien handelt.
15 16
Das Problem liegt eher im Bereich des Datenschutzes und insbesondere bei den Third Party Cookies.5 Die ersten beiden Parteien sind der Nutzer und der Anbieter der Website, die der Nutzer aufruft. Meist hat der WebsiteAnbieter allerdings Werbebanner und als Partner einen Vermarkter. Dieser Vermarkter kann nun – von seinem Server aus – beim Nutzer ein Cookie setzen. Wenn ein Vermarkter genügend Partner hat, findet er über eine andere Webseite, die der Nutzer dann ansurft, diesen Nutzer wieder, da das Cookie vom Server des Vermarkters gesetzt wurde. Daraus ergibt sich – schrittweise – ein Abbild des Surfverhaltens. Auch wenn der Vermarkter damit noch keine persönlichen Informationen über den Nutzer erhält, da seine Identität nicht über das Cookie herausgefiltert werden kann, ist dies dennoch nicht unbedenklich.
17 18 19 20
Die Browserhersteller haben größtenteils darauf reagiert und erlauben dem Nutzer, Cookies von Drittanbietern gesondert zu sperren. Abbildung 18.1: Im Internet Explorer können Sie unter INTERNETOPTIONEN auf der Registerkarte DATENSCHUTZ/ ERWEITERT Cookies von Drittanbietern sperren.
21 22 23 24
5
Eine Eindeutschung ergibt in etwa: Cookies einer dritten Partei oder Cookies von Drittanbietern.
513
Cookies
Für den Webprogrammierer ist die Sicherheitsdiskussion daher von Bedeutung, da er damit rechnen muss, dass einige Nutzer Cookies abschalten oder zumindest einzeln manuell bestätigen und nur Cookies akzeptieren, wenn sie von der Notwendigkeit und der Seriosität einer Website überzeugt sind.
INFO
In Abschnitt 18.3 »Cookietest« zeigen wir Ihnen, wie Sie feststellen können, ob ein Browser Cookies unterstützt. Sie sollten den Nutzer aber darauf hinweisen, dass und zu welchem Zweck Sie Cookies verwenden. Insbesondere bei Shop-Anwendungen, wo Cookies oft unumgänglich sind, sollten Sie das Thema offensiv angehen und Ihren Kunden reinen Wein einschenken. Eine weitere Sicherheitsdiskussion rund um Cookies entspannt sich bei der Sicherheit in der Programmierung. Wenn der Entwickler so genannte Cross Site Scripting-Lücken (XSS) zulässt, kann ein Angreifer über Links, die er Nutzern z.B. per E-Mail zukommen lässt, die Cookies des Nutzers auslesen. Zwar geht das nur für die Cookies der jeweiligen Website, aber wenn dort beispielsweise eine Session-ID enthalten ist, kann er die Sitzung des Nutzers übernehmen. Diese Lücke hat allerdings nichts mit dem Cookie an sich zu tun, sondern entspringt meist schlampiger Programmierung. Mehr dazu lesen Sie in Kapitel 30 »Sicherheit«.
18.1.3 Cookies im Browser Ein Webprogrammierer sollte den Spuren seiner Cookies auch im Browser folgen können, um die Anwendungen gut testen zu können. Daher finden Sie in diesem Abschnitt Informationen, wo die wichtigsten Browser Cookies speichern und wie sie eingesetzt werden können. Internet Explorer Der Internet Explorer legt ein eigenes Verzeichnis für die Cookies an. Dort werden die Cookies jedoch erst nach dem Ende der Browsersitzung, also wenn der Browser geschlossen wurde, gespeichert. Davor sind die Cookies nur im Hauptspeicher vorhanden. In älteren Windows-Versionen ist dieses Verzeichnis WINDOWS/COOKIES. Unter Windows 2000 und XP befinden sich die Cookies im Verzeichnis DOKUMENTE UND EINSTELLUNGEN/[NUTZERNAME]/COOKIES. Jedes einzelne Cookie ist eine .txt-Datei. Die Sicherheitseinstellungen im Internet Explorer sind recht differenziert. Als Programmierer sollten Sie nie vergessen, dass der Nutzer Cookies sehr exakt steuern kann. Sie finden die Einstellungen im Menü EXTRAS/INTERNETOPTIONEN. Dort lassen sich unter ALLGEMEIN die Cookies löschen. Alternativ geht das im Internet Explorer 7 auch direkt über EXTRAS/BROWSERVERLAUF LÖSCHEN. Auf der Registerkarte DATENSCHUTZ kann die Cookieverwaltung sehr detailliert eingestellt werden. Das Sicherheitslevel lässt sich per Schieberegler steuern. Unter ERWEITERT hat der Nutzer die Möglichkeit, sämtliche 514
Geschichte
Parameter einzeln einzustellen. Auch für einzelne Websites lassen sich Cookie-Einstellungen vornehmen. Die meisten Nutzer nehmen diese Einstellungen jedoch nicht vor oder kennen sie nicht. Daher haben viele Nutzer die Einstellung MITTEL, die die Standardeinstellung ist. Abbildung 18.2: Ein Cookie von Amazon
14 15 16 17 18
Wenn Sie im Internet Explorer Cookies ausschalten, also das höchste Sicherheitsniveau wählen, können Sie immer noch aus einer lokalen Datei Cookies anlegen.
19
HALT
20
Mozilla, Firefox und Co. Der Mozilla bzw. Firefox bietet einen eigenen COOKIE-MANAGER, mit dem Sie die Cookies verwalten.
21
Die Einstellungen für Cookies finden Sie unter EXTRAS/EINSTELLUNGEN. Im Register DATENSCHUTZ enthält der Eintrag COOKIES die verschiedensten Einstellungen. Durch Anklicken des Menüpunkts COOKIES ANZEIGEN öffnet sich der COOKIE-MANAGER, der es Ihnen ermöglicht, einzelne oder alle Cookies zu löschen.
22 23
In Kapitel 30 »Sicherheit« erfahren Sie mehr zu den Privatsphärenstufen und wie Sie in JavaScript damit umgehen. REF
24
Der Rest Der Opera steuert die Cookies über EXTRAS/EINSTELLUNGEN und dort ERWEITERT/COOKIES. Eine Verwaltungsoberfläche verbirgt sich hinter der Schaltfläche COOKIES VERWALTEN. Dort lassen sich Cookies für jede einzelne Website einstellen. Im Konqueror sind die benötigten Sicherheitseinstellungen für Cookies unter EINSTELLUNGEN/KONQUEROR EINRICHTEN zu finden. Ein hübscher Kuchen neben dem Eintrag COOKIES weist den Weg. Auf der Registerkarte VERWALTUNG können Sie hier auch die bestehenden Cookies ändern. 515
Cookies
18.2 Mit Cookies arbeiten Für Cookies gibt es in JavaScript ein eigenes Objekt namens cookie.6 Es ist ein Unterobjekt von document und steuert alle Cookies, die die Seite setzt oder ausliest.
18.2.1 Setzen und auslesen Ein Cookie ist schnell gesetzt. Weisen Sie document.cookie einfach ein Cookie als Wert zu. Die Syntax sieht wie folgt aus: document.cookie = "Cookiename=Wert";
Auf den Cookienamen folgt der Wert. Natürlich sind für ein Cookie auch weitere Angaben möglich, die Sie in den folgenden Abschnitten kennen lernen werden. Um das Cookie wieder auszulesen, müssen Sie erneut auf document.cookie zugreifen. Der Wert des Cookies muss allerdings immer in URL-kodierter Form vorliegen. Am einfachsten geschieht die Umwandlung mit der Funktion escape() oder encodeURIComponent(). Für die Rückumwandlung steht unescape() oder decodeURIComponent() zur Verfügung.7 Im folgenden Beispiel schreibt der Nutzer einen Text in ein Textfeld. Dieser Text wird beim Anklicken der Schaltfläche COOKIE SETZEN in einem Cookie mit dem Namen cookie1 abgelegt, nachdem er zuvor in URL-Kodierung umgewandelt wurde. Klickt der Nutzer anschließend auf die Schaltfläche COOKIE AUSLESEN, wird das Cookie wieder ausgelesen und mit unescape() in den ursprünglichen Zustand umgewandelt. Listing 18.1:
Ein Cookie setzen und wieder auslesen (cookie.html)
Cookies 6 7
516
Beachten Sie, dass im Gegensatz zu vielen Kollektionen wie beispielsweise window.frames[] oder document.forms[] document.cookie im Singular verwendet wird. Diese Funktionen finden Sie in Abschnitt 4.3.4 »URLs«.
Mit Cookies arbeiten
14 Abbildung 18.3: Der Klick auf den Button erzeugt ein neues Cookie.
15 16 17 18 19
Abbildung 18.4: Im Firefox ist das Cookie im COOKIEMANAGER zu sehen.
20 21 22 23 24
517
Cookies
Abbildung 18.5: Das Cookie wieder auslesen
Beim Auslesen (siehe Abbildung 18.5) erscheint allerdings nicht nur der Cookiewert, sondern auch der Cookiename. Um diesen abzuschneiden, können Sie mit der Methode substring() die Buchstaben aus dem Cookie auslesen, die nach dem Namen folgen: Listing 18.2:
Den Namen abschneiden (cookie_wert.html)
function auslesen() { document.formular.ausgabe.value = unescape(cookie_ausgabe()); } function cookie_ausgabe() { return document.cookie.substring(8, document.cookie.length) }
Dieses Skript ist von der Länge des Cookienamens abhängig. Abbildung 18.6: Nur der Wert des Cookies wird ausgelesen.
518
Mit Cookies arbeiten
Mehr Informationen zu substring() und anderen Methoden für Strings finden Sie in Kapitel 7 »Arrays und Strings«. REF
Mehrere Cookies Die Probleme beginnen jedoch, wenn vom Dokument mehrere Cookies gesetzt wurden. Beim Auslesen erscheinen mehrere Cookies hintereinander:
14
document.cookie = "cookie1=Cookie1"; document.cookie = "cookie2=Cookie2";
15
ergibt cookie1=Cookie1; cookie2=Cookie2
als Ausgabe. Um die Werte der Cookies auszulesen, benötigen Sie also eine etwas umfangreichere Hilfsfunktion.
16
Die folgende Funktion liest den Wert eines Cookies mit beliebigem Namen aus. Der Name des Cookies muss der Funktion als Parameter übergeben werden:
17 18
function cookie_ausgabe(name) { var daten = document.cookie; for (i = 0; i < daten.length; i++) { if (daten.substring(i, i+name.length) == name) { var ende = daten.indexOf(";",i+name.length); if (ende == -1) ende = daten.length; return daten.substring(i+name.length+1, ende); } } }
19 20 21
Die Hilfsfunktion besteht aus einer Schleife, die folgende Funktionen durchführt: ■ ■ ■
■
22
In einer Schleife werden alle Positionen (Buchstaben) von document. cookie durchlaufen. Für jede Position wird überprüft, ob die nachfolgenden Zeichen dem gesuchten Cookienamen entsprechen. Ist dies der Fall, stellt eine Suche nach der Position des abschließenden Strichpunkts das Ende des Cookiewerts fest. Ist kein Strichpunkt vorhanden (ende also -1), folgt anschließend kein neuer Cookiename. Das Ende entspricht also dem Ende von document.cookie. Zurückgegeben wird dann der Text des Werts, dessen Anfang eine Position hinter dem Cookienamen beginnt und dessen Ende vorher festgestellt wurde.
23 24
Diese Funktion klappt nicht, wenn ein Cookiename den Namen des gesuchten Cookies enthält, beispielsweise wenn ein Cookie cookie heißt und das andere cookie2. Entweder man hält sich in diesem Fall also an eine Namenskonvention oder man prüft in diesem Fall genauer. 519
Cookies
Im Download-Archiv finden Sie im Ordner code\kap18 ein vollständiges Beispiel mit dieser Hilfsfunktion unter cookie_allgemein.html. WWW
Abbildung 18.7: Der Nutzer setzt zwei Cookies und nur der Wert von einem wird ausgelesen.
HALT
Sie sollten so wenig Cookies wie möglich verwenden, da Nutzer möglicherweise Cookies manuell bestätigen und von zu vielen Cookies gestört werden. Mehr zu diesem Thema erfahren Sie in Abschnitt 18.2.4 »Mehrere Informationen in einem Cookie«. Temporäre oder persistente Cookies Ein Cookie kann – entsprechend seiner Beschränkungen – weitere Informationen enthalten. Der wichtigste Parameter ist sicherlich das Ablaufdatum: document.cookie = ("Cookiename=Cookiewert;expires=Zeitpunkt");
In den bisherigen Beispielen wurde das Ablaufdatum nicht angegeben. In diesem Fall handelt es sich um ein temporäres Cookie, das gelöscht wird, wenn der Nutzer den Browser schließt. Diese Art Cookie gelangt auch nie in die Textdatei (oder .dat-Datei) auf der Festplatte des Nutzers, da sie – während der Browser geöffnet ist – im Hauptspeicher verbleibt. Mit Ablaufdatum wird ein Cookie dagegen zu einem persistenten oder auch haltbaren Cookie. Der Zeitpunkt, zu dem das Cookie abläuft, muss dabei als GMT-String angegeben werden.8 Dies erreichen Sie mit einem neuen Datumsobjekt, das mit toGMTString() in GMT umgewandelt wird. Die beste Methode für das Datum ist, das aktuelle Tagesdatum zu verwenden und eine beliebige Anzahl an Stunden oder Tagen zu addieren. Kürzere Zeitspannen sind nicht empfehlenswert, da das Cookie unter Umständen vor dem Ende der Nutzersitzung ausläuft. 8
520
Mehr Informationen zur Arbeit mit Daten erhalten Sie in Abschnitt 6.1.2 »Date«.
Mit Cookies arbeiten
In der folgenden Funktion läuft das Cookie zwei Tage ab dem Zeitpunkt, zu dem es gesetzt wurde: Listing 18.3:
Ein Cookie mit Ablaufdatum in zwei Tagen (cookie_expires.html)
function setzen() { var datum = new Date(); datum.setTime(datum.getTime() + 2* 24 * 60 * 60 * 1000); document.cookie = "cookie1=" + escape(document.formular.eingabe.value) + ";expires=" + datum.toGMTString(); }
14
Abbildung 18.8: Der COOKIE-MANAGER des Firefox zeigt an, wann das Cookie abläuft.
15 16 17 18 19 20 21 22
Wenn Sie ein Cookie anzeigen, wird das Ablaufdatum nicht mit angegeben. Das Ablaufdatum von Cookies kann in JavaScript nicht ausgelesen werden, da es keine Eigenschaft des cookie-Objekts ist. Dies funktioniert in serverseitigen Programmiertechniken wie ASP.NET oder PHP.
23 HALT
24
Pfade, Domains und Sicherheitseinstellungen Die Beschränkungen für Cookies erlauben auch, Cookies für einen bestimmten Pfad oder eine Domain zu setzen. Entsprechend stehen drei weitere Parameter in document.cookie zur Verfügung:
521
Cookies
document.cookie = ("Cookiename=Cookiewert ;expires=Zeitpunkt ;path=Pfad ;domain=Domain ;secure");
Die Einstellungen funktionieren folgendermaßen: ■
Ist kein Pfad angegeben, dürfen die Cookies aus dem Standardpfad gelesen werden, in dem sich das Dokument befindet, das die Cookies setzt. Sie sollten einen Pfad nur dann setzen, wenn Sie es mit einem Dokument in einem anderen Pfad bearbeiten wollen. Beachten Sie aber, dass das Originaldokument nach dem Setzen keinen Zugriff mehr auf das Cookie besitzt.
Setzen Sie den Pfad auf path=/, um das Cookie für die gesamte Domain zugänglich zu machen. TIPP
■
■
TIPP
Die Domain muss zumindest zwei Elemente – Domain und Top-LevelDomain – enthalten, nur der Host kann weggelassen werden. .mut.de wäre also eine gültige Domain. Wenn Sie eine Domain setzen, kann das Cookie nur von Seiten innerhalb dieser Domain abgefragt werden. Allerdings wird es von vielen Browsern als Third Party Cookie behandelt. Die letzte Einstellung secure legt fest, dass das Cookie beispielsweise nicht von serverseitigen Skripten verändert werden darf, obwohl sie die korrekte Domain und den richtigen Pfad besitzen. Wenn Sie den Parameter weglassen, haben auch serverseitige Skripte Zugriff. Dies ist in der Praxis meist empfehlenswert (siehe Abschnitt 18.4 »Serverseitig, clientseitig oder Ajax?«).
Im Web gibt es viele Hilfsfunktionen und Klassen, um mit Cookies noch bequemer zu arbeiten. Die meisten davon gehen auf Bill Dortch zurück, dessen eigene Seite jedoch nicht mehr existiert. Sie finden einen Ableger beispielsweise unter http://www.webdeveloper.com.
18.2.2 Ändern Ein Cookie zu ändern ist sehr einfach. Sie setzen es unter dem gleichen Namen neu und das alte Cookie wird überschrieben. document.cookie = ("cookie1=Wert1"); document.cookie = ("cookie1=Wert2");
Nach diesen zwei Zeilen hat cookie1 den Inhalt Wert2.
18.2.3 Löschen Auch das Löschen folgt einer einfachen Idee. Setzen Sie das Ablaufdatum des Cookies auf einen zurückliegenden Zeitpunkt:
522
Cookietest
Listing 18.4: Das Cookie wird gelöscht (cookie_loeschen.html). function loeschen() { var datum = new Date(); datum.setTime(datum.getTime() - 1); document.cookie = "cookie1=;expires=" + datum.toGMTString(); }
14
Alternativ zu einem Zeitpunkt direkt vor der aktuellen Uhrzeit können Sie natürlich auch ein fixes zurückliegendes Datum verwenden. TIPP
15
18.2.4 Mehrere Informationen in einem Cookie Da die Zahl der Cookies beschränkt ist und Sie zu viele Cookies ohnehin vermeiden sollten, um die Nerven des Nutzers nicht unnötig zu strapazieren, besteht häufig die Notwendigkeit, mehrere Informationen in ein Cookie zu packen. Der einfachste Ansatz ist, aus den Informationen einen String zu bilden und die Informationen mit einem Trennzeichen zu trennen, das in den Infos selbst nicht vorkommt. Alternativ können Sie natürlich auch die aus Ajax bekannte JSON-Notation für Objekte und Arrays verwenden (siehe Kapitel 12 »JSON«).
16
Wenn Sie die Cookieverbindung beispielsweise in einem Warenkorb verwenden, können Sie überprüfen, ob der String eine Länge unter 4.096 Zeichen besitzt. Wenn dies nicht der Fall ist, sollten Sie ihn teilen und die weiteren Informationen in das nächste Cookie schreiben.
19
17 18
TIPP
20
18.3 Cookietest
21
Wenn Ihre Anwendung auf Cookies angewiesen ist, sollten Sie testen, ob der Browser des Nutzers Cookies unterstützt. Eine Abfrage über das navigator-Objekt ist hier nicht möglich, da Cookies vom Nutzer auch deaktiviert werden können.
22
Daher ist es am einfachsten, ein Testcookie zu setzen, zu überprüfen, ob es vorhanden ist, und es anschließend zu löschen. Dies erledigt das folgende Skript: Listing 18.5:
23 24
Ein einfacher Cookietest (cookie_test.html)
Cookietest
Dieses Skript testet, ob Cookies aktiviert sind...
HALT
Wenn Sie das Skript ausführen und alle Cookies sperren, müssen Sie zuvor die angelegten Cookies löschen, falls bereits ein Cookie mit dem Namen Test vorhanden ist.
Abbildung 18.9: Der Cookietest hat ergeben, dass Cookies deaktiviert sind.
WWW
Entsprechend dem Ergebnis des Cookietests können Sie natürlich auch auf verschiedene Seiten weiterleiten. Ein entsprechendes Skript mit dem Namen cookie_test_weiter.html finden Sie im Download-Archiv im Verzeichnis code\kap18.
18.4 Serverseitig, clientseitig oder Ajax? Mit serverseitigen Programmiersprachen und -techniken wie PHP und ASP.NET können Sie natürlich auch Cookies setzen. Die Cookies werden dabei immer im Header des HTTP-Protokolls mitgeschickt. Diese Methode hat jedoch einen gravierenden Nachteil: Cookies können nicht sofort, nachdem sie gesetzt wurden, ausgelesen werden. Ein Cookietest funktioniert also nur über Umwege. Dafür bieten serverseitige Programmiersprachen häufig eigene Eigenschaften für die einzelnen Parameter von Cookies.
524
Serverseitig, clientseitig oder Ajax?
Aus den Vor- und Nachteilen leiten sich folgende Empfehlungen ab: ■ ■
Ein Cookietest ist in JavaScript wesentlich einfacher.9 Sie müssen beim Nutzer jedoch JavaScript voraussetzen. Sie können Cookies mit JavaScript einfach aus Formularen – oftmals nach einer Vollständigkeitsüberprüfung – anlegen und diese dann natürlich auch per serverseitiger Programmiersprache abfragen. Diese Mischung kommt in der Praxis recht häufig vor.
14
Natürlich können Sie einen Warenkorb auch nur mit JavaScript realisieren oder einen Cookietest in einer serverseitigen Programmiersprache schreiben. Die Kombination liefert jedoch häufig die besten und schnellsten Resultate.
15 16
Für Ajax-Anwendungen gilt das ganz besonders. Soll hier beispielsweise per Cookie der Weg des Nutzers nachvollzogen werden, kann das clientseitig oder serverseitig geschehen. Aus JavaScript können Sie allerdings auch auf die von der serverseitigen Technologie gesetzten Cookies zugreifen. Umgekehrt lassen sich serverseitige Cookies per JavaScript auslesen. Technologisch wichtig zu wissen ist hier nur, dass Cookies für die Domain bei jedem HTTP-Request im HTTP-Header mitgeschickt werden. Das heißt, auch bei Ajax-Requests landen die Cookies bei der serverseitigen Technologie. Die einzige Beschränkung ist die für Cookies geltende Beschränkung auf eine Domain oder einen Pfad.
17 18 19 20 21 22 23 24
9
Sie umgehen nicht nur die soeben genannten Nachteile. Die Microsoft-Server PWS (Personal Web Server) und IIS (Internet Information Service) erlauben Ihnen in manchen Versionen nicht, ein Cookie zu setzen und im gleichen Schritt eine Weiterleitung einzufügen.
525
Inhalt
19 Frames 14
Zwar sind Frames heute als Mittel zum Layout und zur Seitenaufteilung eher nicht mehr empfohlen und gegenüber den CSS-Layouts in den Hintergrund gerückt, dennoch gibt es noch viele Anwendungen mit Frames und auch noch sinnvolle Einsatzgebiete. Dies gilt vor allem für die Frames im Dokument, die so genannten iFrames (siehe Abschnitt 19.4 »iFrames« ).
15
Normale Frames1 sind eigentlich Fenster im Fenster. Die Struktur der Frames wird im Frameset definiert. In jedem Frame wird eine eigene HTMLDatei geladen.
17
Das folgende Beispiel zeigt ein Frameset mit zwei Frames, die als einzelne Spalten (cols) im -Tag mit einer Breite von 20 % bzw. 80 %2 angeordnet sind:
18
Listing 19.1:
16
19
Ein HTML-Frameset mit zwei Frames (frameset.html)
Frameset
20
21
22
Die Dateien für die zwei Frames navigation.html und inhalt.html finden Sie im Ordner code\kap19 im Download-Archiv.
23 WWW
Die in diesem Beispiel gewählte Aufteilung ist in der Praxis häufig zu finden: links ein Navigationsframe und rechts der Inhaltsframe. Oftmals kommt als dritter Frame noch ein Frame oben oder unten hinzu.
2
Ein Beitrag zur Sprachverwirrung: Die deutsche Übersetzung von Frames ist Rahmen; es handelt sich dabei um einen Rahmen mit Inhalt. Der Rahmen selbst ist eigentlich eher ein Rand, was dem englischen Begriff border entspricht. Alternativ kann die Breite in Pixel angegeben werden. In diesem Fall ist keine Einheit notwendig.
527
ndex
1
24
Frames
Abbildung 19.1: Ein einfaches Frameset mit zwei Frames
Frames sind zum übersichtlichen Organisieren, beispielsweise durch Trennen von Navigation und Inhalt, sehr praktisch und bereits in Uraltbrowsern wie Netscape 2 und Internet Explorer 3 verfügbar. Sie haben allerdings auch Nachteile: ■
■
■
In manchen Browsern (Netscape, Mozilla und Internet Explorer vor Version 5) können Nutzer Framesets nicht korrekt als Bookmark anlegen. Das übergeordnete Frameset wird »gebookmarkt«, der geänderte Inhaltsframe bleibt unberücksichtigt. Frames erfordern mehrere HTML-Dateien, was die Struktur einer Website unübersichtlich werden lässt, wenn nicht auf vernünftige Namenskonventionen und Ordnerstrukturen geachtet wird. Größe und Positionierung von Frames variieren in den verschiedenen Browsern leicht und erfordern einige Tricks zum Ausbügeln.
19.1
Struktur und Zugriff
In jedem HTML-Dokument steht in JavaScript die Kollektion window. frames[Index]3 zur Verfügung. Diese Kollektion enthält alle Frames des aktuellen HTML-Dokuments. Das erste Frame erhält den Index 0. Die folgenden Frames werden der Reihe nach durchnummeriert. window.frames[2]
greift also auf den dritten Frame zu. 3
528
Beachten Sie, dass die Kollektion window.frames[]den englischen Plural verwendet, während document.cookie im Singular verwendet wird. Zwar handelt es sich bei document.cookie nicht um eine Kollektion im eigentlichen Sinn, dennoch ist die Fehlergefahr recht groß.
Struktur und Zugriff
Besitzt ein HTML-Dokument kein Frameset mit Frames, enthält die Kollektion auch keinen Wert und hat die Länge 0. Die Länge kann mit der Eigenschaft length ausgelesen werden: window.frames.length
ergibt also die Zahl der Elemente eines Dokuments. Ein Frame kann allerdings nicht nur über den Index angesprochen werden. Eine Alternative ist der Frame-Name, der im name-Attribut angegeben ist:
14
window.navi
15
Obige Zeile greift auf einen Frame mit dem Namen navi zu. Als dritte Alternative lässt sich der Frame-Name auch in der Kollektion window.frames[] angeben.
16
window.frames["navi"]4
17
Da diese Lösung die tippintensivste ist und Programmierer immer faul sind, kommt sie in der Praxis seltener zum Einsatz. Der bisher beschriebene Zugriff über window funktioniert nur in einem Skript in der Datei, die das Frameset enthält. Wie verhält es sich jedoch, wenn Sie beispielsweise aus einer Frame-Datei auf die Datei mit dem Frameset zugreifen möchten? Hier sollte man sich klarmachen, dass Frames in einer Hierarchie angeordnet sind. Die Hierarchie des Framesets aus Listing 19.1 wird in Abbildung 19.2 dargestellt.
18 19 Abbildung 19.2: Die Hierarchie der Beispielseite
20 21 22 23 24
4
Bei dieser Methode können Sie Frame-Namen verwenden, die normalerweise von JavaScript reserviert sind. Empfehlenswert ist dies jedoch nicht.
529
Frames
Innerhalb dieser Hierarchie gibt es drei Eigenschaften, über die ein Zugriff möglich ist: ■ ■
HALT
parent – erlaubt den Zugriff aus dem Frame auf das übergeordnete Frameset. top – gestattet den Zugriff auf das oberste Frameset in der Hierarchie.
Wenn Sie auf das übergeordnete Frameset zugreifen wollen, sollten Sie auch bei der Verwendung von nur einem Frameset immer mit parent arbeiten. Anderenfalls kann es zu Fehlern kommen, wenn Ihr komplettes Frameset auf einer anderen Seite ebenfalls wieder in einem Frameset geladen wird.5 ■
self – verweist auf den aktuellen Frame.
Kommen wir nun zu dem Beispiel aus Listing 19.1 und Abbildung 19.2 zurück. Wenn Sie von Frame 0 auf Frame 1 zugreifen wollen, funktioniert dies folgendermaßen: parent.frames[1]
oder: parent.inhalt
Tabelle 19.1: Frames und ihr Zugriff
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
19.1.1 Ereignisse und Frames Für Frames hält JavaScript einige eigene Ereignisse bereit. Das wichtigste Ereignis ist onload. Es steht für das und das -Tag zur Verfügung und wird aktiv, wenn das gesamte Frameset oder der Inhalt des jeweiligen Frames geladen ist. Tabelle 19.2: Das Ereignis onload für das Frameset und für einzelne Frames
onload (Frameset) onload (Frames)
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Im folgenden Beispiel erscheint für jeden geladenen Frame und für das Frameset eine Meldung. Dies erlaubt auch einen Vergleich, wann welches Ereignis vom Browser abgefeuert wird.
5
530
Das Laden der eigenen Seite in einem fremden Frameset ist häufig unerwünscht. Mehr zu diesem Thema erfahren Sie unter Punkt 19.3 »Frames erzwingen und vermeiden«.
Struktur und Zugriff
Listing 19.2:
Frameset mit Ereignissen (frameset_ereignisse.html)
Frameset
14 15
Wenn Sie die Ergebnisse dieses Beispiels vergleichen, ergibt sich zwischen verschiedenen Browsern eine unterschiedliche Ladereihenfolge:
16
Der Internet Explorer zeigt zuerst an, dass das Frameset geladen wurde. Der Firefox lädt zuerst die beiden Frames. Die Reihenfolge, in der die Frames geladen werden, variiert.
17
In der Praxis ist die Reihenfolge der Ereignisse also unvorhersehbar und kann nicht als Anhaltspunkt dienen.
18
Das onload-Ereignis im Internet Explorer 4 wird leider auch dann gestartet, wenn der Nutzer den Ladevorgang abbricht.
19
■ ■
HALT
20
Die folgende Tabelle zeigt weitere Ereignisse: Ereignis
Tag
Beschreibung
onunload
Tritt auf, wenn das Frameset verlassen wird.
onbeforeprint onafterprint
Ereignis, wenn ein Frameset gedruckt wird oder gedruckt wurde. Existiert nur im Internet Explorer ab Version 5.
onbeforeupdate
Ereignis, bevor sich die im Frame eingebundenen Daten ändern.a Dies ist auf den Internet Explorer ab Version 4 beschränkt.
onafterupdate
Ereignis, nachdem sich die Daten geändert haben.b Es ist ebenfalls auf den Internet Explorer ab Version 4 beschränkt.
onerrorupdate
Ereignis, wenn in der Datenanbindung ein Fehler auftaucht, ebenfalls auf den Internet Explorer ab Version 4 beschränkt.
a.
b.
Tabelle 19.3: Weitere Ereignisse für Frameset und Frames
21 22 23 24
Dieses Einbinden von Daten heißt auch Data Binding. Dabei werden Daten aus DSO-Elementen (Data Source Objects) an Elemente einer Webseite gebunden. Der Internet Explorer besitzt beispielsweise die MSHTML-Control, die Tabular Data Control (TDC) und die XML DSO. Diese Ereignisse gibt es auch für Textfelder (), die Data Binding verwenden.
531
Frames
Tabelle 19.4: Kompatibilität der weiteren Ereignisse
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
onbeforeprint
onafterprint
onunload
onbeforeupdate
onafterupdate
onerrorupdate
19.1.2 Frames formatieren Seit der W3C-Standard den Zugriff auf alle Elemente einer Website erlaubt, gibt es einige neue Möglichkeiten. Mit getElementById("ID") können Sie auch auf - und -Elemente zugreifen.6 Diese Elemente wiederum haben Eigenschaften, mit denen Sie beispielsweise das Aussehen der Frame-Rahmen ändern können. Die Eigenschaften in JavaScript gleichen meist den gleichnamigen HTML-Attributen.
HALT
Diese Art von Zugriff greift nur auf die HTML-Elemente und zu, nicht aber auf den Frame selbst mit seinen Inhalten wie JavaScript-Funktionen, Stilangaben usw. Eigenschaften für Das -Tag besitzt vier Eigenschaften für die Rahmen der Frames. Diese Eigenschaften lassen sich zwar auch setzen, funktionieren jedoch im Internet Explorer ab Version 4 – dem einzigen Browser, der sie unterstützt – bei bereits geladenem Frameset und Frames nicht direkt.7 ■ ■ ■ ■
6 7
532
border – gibt die Dicke des Rahmens in Pixeln an. borderColor – gibt die Farbe des Framesets an. Dies entspricht dem HTML-Attribut bordercolor. frameBorder – gibt an, ob ein Rahmen vorhanden ist (yes bzw. 1 oder no bzw. 0). Dies entspricht dem HTML-Attribut frameborder. frameSpacing – setzt den Zwischenraum zwischen Frames in Pixeln. Dies entspricht dem HTML-Attribut framespacing.
Frameset und Frame sind hier Browserobjekte, auf die Sie per JavaScript zugreifen. Dieser Bug schränkt die Verwendbarkeit dieser vier Eigenschaften stark ein. Sie sehen die Änderung nur, wenn das Browserfenster nicht mehr im Vordergrund steht und anschließend erneut in den Vordergrund geholt wird. Vorher wirken sich die Änderungen nicht aus.
Struktur und Zugriff
NS4.x
M/FF
border, borderColor, frameBorder und frameSpacing
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 19.5: border, borderColor, frameBorder und frameSpacing
14
Die Breite bzw. die Höhe von Frames ändern zwei weitere Eigenschaften des -Tags. ■
■
15
cols – ändert die Spaltenverteilung. Als Wert tragen Sie einen Wert ein, den Sie auch dem cols-Attribut des -Tags zuweisen würden. cols ist natürlich nur sinnvoll, wenn Sie ein Frameset mit Spalten ändern. rows – wird für ein Frameset mit Reihen verwendet. Hier tragen Sie den gewünschten Wert für die Reihengrößen ein.
Sollten Sie die Zahl der Frames erhöhen, wird ein leerer Frame hinzugefügt, verringern Sie die Zahl der Frames, fallen immer die letzten Frames zuerst heraus. In der Praxis werden die beiden Eigenschaften allerdings selten eingesetzt. NS4.x
cols und rows
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
16 17 18
TIPP
Tabelle 19.6: cols und rows
19 20
Das folgende Beispiel ändert die Breite der Frames mittels eines Buttons im Navigationsframe. Im Frameset muss gegenüber Listing 19.1 nicht viel geändert werden. Lediglich das Frameset benötigt eine ID.
21
Das -Tag besitzt hier eine ID (frameset_eigenschaften.html).
22
Listing 19.3:
Frameset mit Eigenschaften
23 24
533
Frames
Im Navigationsframe fügen Sie eine Schaltfläche ein und rufen eine Funktion auf, die die Breite der Spalten ändert: Listing 19.4: Das Skript ändert die Breite der Frames (navigation_eigenschaften.html). Navigation Navigation Breite ändern
Abbildung 19.3: Ein Anklicken der Schaltfläche …
534
Struktur und Zugriff
Abbildung 19.4: … verbreitert den Navigationsframe.
14 15 16 17 18 Um Internet Explorer 4-konform zu programmieren, müssten Sie im Skript aus Listing 19.4 zusätzlich document.all verwenden, um auf das FramesetObjekt zuzugreifen. Im Download-Archiv im Ordner code\kap19 finden Sie unter navigation_eigenschaften_ie4.html das Listing mit dieser Erweiterung.
19
WWW
20 Wenn Sie für eine Größenanpassung der Frames volle Browserkompatibilität benötigen, sollten Sie ein zweites Frameset mit geänderten Maßen verwenden und beim Anklicken der Schaltfläche oder eines Links das neue Frameset laden. Der Nachteil besteht darin, dass Sie beispielsweise in einem Navigationsframe nicht so einfach feststellen können, welche Inhaltsdatei aktuell geladen ist.
21
TIPP
22
Eigenschaften für
23
Für das Frame-Objekt gibt es mehr Eigenschaften als für Framesets. Hier ist die Browserunterstützung jedoch sehr unterschiedlich und in der Praxis werden cols und rows des Framesets am häufigsten eingesetzt.
24
Die folgende Tabelle gibt eine Übersicht über die wichtigsten Frame-Objekte.
535
Frames
Tabelle 19.7: Wichtige Eigenschaften des Frame-Objekts
Eigenschaft
Beschreibung
allowTransparency
Gibt als Wahrheitswert an, ob Transparenz erlaubt ist (true) oder nicht (false). Der Wert für die Hintergrundfarbe des Frames ist damit automatisch transparent. Die Hintergrundfarbe des Fensters scheint durch.
borderColor
Ändert die Rahmenfarbe (siehe Abschnitt »Eigenschaften für « unter Punkt 19.1.2). Funktioniert im Internet Explorer nicht sofort, wenn das Frameset bereits geladen ist. Dieses Objekt ist in der Praxis also nur eingeschränkt einsetzbar.
frameBorder
Erlaubt Rahmen (»yes« bzw. 1) oder verbietet (»no« bzw. 0) sie. Funktioniert im Internet Explorer nicht sofort, wenn das Frameset bereits geladen ist. Es ist in der Praxis also nur eingeschränkt einsetzbar (siehe borderColor).
height
Enthält die nur lesbare Höhe des Frames. Wird häufig dazu verwendet, einen Pixelwert zu erhalten, der dann für rows verwendet werden kann.
width
Enthält die Breite. Nur auslesbar, nicht setzbar.
marginHeight
Gibt den Abstand zwischen Inhalt und Frame-Rahmen in der Vertikalen an.
marginWidth
Gibt den Abstand zwischen Inhalt und Frame-Rahmen in der Horizontalen an.
noResize
Verhindert bei true, dass der Nutzer den Frame in der Größe ändern kann. Dies entspricht dem noresize-Attribut.a Diese Eigenschaft hat jedoch keine Auswirkungen auf die Größenänderungen mit den Eigenschaften cols und rows des Frameset-Objekts.
scrolling
Erlaubt (»yes« bzw. 1) oder verbietet (»no« bzw. 0) Scrolling. auto lässt den Browser entscheiden. Er blendet Scrollbalken ein, wenn der Inhalt über die Grenzen des Frames hinausragt.
src
Ändert die Quelle des Frames. Dies erlaubt Ihnen, das Dokument im Frame zu ändern.
a.
Tabelle 19.8: Wichtige Eigenschaften des Frame-Objekts
Das zugehörige Ereignis ist onresize. Für Frames existiert dieses Event ab Netscape Navigator 6, Firefox und Internet Explorer 4. Für das gesamte Fenster (window) ist das Ereignis bereits im Netscape Navigator 4.x enthalten.
NS4.x
M/FF
IE5
allowTransparency
IE5.5
IE6
IE7
height
width
borderColor frameBorder
536
IE4
Op
SF/KQ
marginHeight
marginWidth
Struktur und Zugriff
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
noResize
scrolling
src
Tabelle 19.8: Wichtige Eigenschaften des Frame-Objekts (Forts.)
14 Das folgende Beispiel verwendet src, um die Quelle des Navigationsframes zu ändern: Listing 19.5:
15
Die Breite mit src ändern (navigation_frame_eigenschaften.html)
Navigation Navigation Quelle ändern
16 17 18 19 Abbildung 19.5: Ein Anklicken der Schaltfläche …
20 21 22 23 24
537
Frames
Abbildung 19.6: … ändert den Inhalt des linken Frames.
19.1.3 Verschachtelte Framesets Wenn Sie Frames komplexer ineinander verschachteln wollen, gibt es zwei Lösungen: ■
Sie können verschachtelte Framesets in einer Datei verwenden. Im folgenden Beispiel wird das Dokument mit einem Frameset vertikal zweigeteilt. Der zweite Frame ist jedoch wiederum ein Frameset, das eine horizontale Zweiteilung vornimmt:
Listing 19.6: Ein verschachteltes Frameset in einer Datei (frameset_verschachtelt.html) Verschachteltes Frameset
538
Struktur und Zugriff
Abbildung 19.7: Ein verschachteltes Frameset
14 15 16 17 18 ■
19
Mehrere verschachtelte Dateien mit Framesets. Im folgenden Beispiel wird dasselbe Frameset wie in Listing 19.6 realisiert – nur auf zwei Dateien verteilt.
Listing 19.7:
20
Das obere Frameset (frameset_verschachtelt_mehrere.html)
Verschachteltes Frameset
21 22 23
Listing 19.8: Das verschachtelte Frameset (frameset2.html)
24
Frameset 2
539
Frames
Das Ergebnis dieser zweigeteilten Verschachtelung entspricht dem in Abbildung 19.7 dargestellten Frameset. JavaScript-Zugriff Für den JavaScript-Zugriff macht die Art der Verschachtelung durchaus einen Unterschied. Wollen Sie aus einem Frame auf das oberste Frameset zugreifen, ist bei einem verschachtelten Frameset in einer Datei die Angabe parent ausreichend. Dies illustriert das folgende Beispiel. Aus dem Frame werbung kann der Nutzer hier die Breite der vertikalen Frames ändern: Listing 19.9: Der Zugriff bei einem verschachtelten Frameset in einer Datei (werbung_js.html) Werbung Werbung Breite ändern
Wenn Sie stattdessen bei einem über zwei Dateien verteilten Frameset auf das oberste zugreifen wollen, sieht das Beispiel wie folgt aus: Listing 19.10: Ein verschachteltes Frameset mit mehreren Dateien (werbung_mehrere_js.html) top.document.getElementById("fs1").cols = "40%,*";
Das Resultat der beiden Methoden (siehe Abbildung 19.8 und Abbildung 19.9) ist dasselbe. In der Praxis erweist sich ein verschachteltes Frameset in einer Datei meist als zweckmäßig. Sie sollten jedoch darauf achten, allen Framesets und Frames unterschiedliche IDs zuzuteilen.
540
Struktur und Zugriff
Abbildung 19.8: Ein Anklicken des Buttons …
14 15 16 17 18 Abbildung 19.9: … vergrößert den linken Frame.
19 20 21 22 23 24
Die kompletten Beispieldateien finden Sie im Download-Archiv im Ordner code\kap19\verschachtelt. Als weitere Ausbaumöglichkeit können Sie die Internet Explorer 4-Kompatibilität mit document.all ergänzen (siehe Beispiel navigation_eigenschaften_ie4.html im Ordner code\kap19).
WWW
541
Frames
INFO
Alternativ zu parent.parent. könnten Sie hier auch top verwenden. Dies funktioniert jedoch nicht, wenn das oberste Frameset – beispielsweise auf einer fremden Seite – in ein weiteres Frameset geladen wird. Wenn Sie top verwenden, sollten Sie also verhindern, dass das oberste Frameset in einen anderen Frame geladen werden kann (vergleiche Abschnitt 19.3 »Frames erzwingen und vermeiden«).
19.2 Häufige Anwendungen und Tricks Dieser Abschnitt enthält einige der wichtigsten Anwendungen von JavaScript für Frames und verrät interessante und beachtenswerte Tricks.
19.2.1 Frames und Links Mehrere Frames mit neuen Inhalten zu versehen gehört zu den häufigsten und wichtigsten Anwendungen. Dazu ändern Sie lediglich die URLs für mehrere Frames in einem Skript. Das folgende Frameset zeigt dies beispielhaft: Listing 19.11: Das Frameset mit name-Attributen (frameset_link.html) Verschachteltes Frameset
In der Navigationsleiste fügen Sie einen JavaScript-Link hinzu. Das zugehörige Skript übernimmt zwei – als Parameter übergebene – URLs und verwendet diese, um sie in den Frames neu zu öffnen: Listing 19.12: Zwei Links in einem Fenster (navigation_link.html) Navigation Navigation Markt+Technik
14
Alle Beispieldateien finden Sie im Ordner code\kap19\link.
15 WWW
Abbildung 19.10: Ein Anklicken des Links …
16 17 18 19 20 21 22
Das Skript aus Listing 19.12 funktioniert in allen Browsern aber nur, wenn JavaScript aktiviert ist. Alternativ können Sie die Funktion auch beim onclick-Ereignis aufrufen, um eine Fehlermeldung zu vermeiden. Wenn Sie ganz ohne JavaScript auskommen möchten, müssen Sie ein komplett neues Frameset laden.
23 TIPP
24
543
Frames
Abbildung 19.11: … öffnet zwei neue Dokumente.
19.2.2 Unsichtbare Frames – eine Ajax-Alternative? In HTML-Tabellen werden häufig 1 * 1 Pixel große transparente GIF-Dateien eingefügt und auf beliebige Abstände skaliert. Andere Grafiken können vorgeladen werden, wenn sie mit der Größe von 1 * 1 Pixel in das HTML-Dokument eingefügt werden. Diese beiden Tricks finden eine Entsprechung bei Frames: Sie können mit einem nur 1 Pixel hohen8 Frame einen so genannten versteckten Frame erzeugen. In diesem Frame lassen sich Daten zwischenspeichern oder Skripte anlegen. Ein Frameset mit verstecktem Frame könnte beispielsweise wie folgt aussehen: Listing 19.13: Ein versteckter Frame (frame_versteckt.html) Frameset
8
544
Manche Browser akzeptieren einen 0 Pixel hohen Frame nicht.
Häufige Anwendungen und Tricks
In der Tat waren unsichtbare Frames lange Zeit eine Art Ajax-Alternative. Man konnte im unsichtbaren Frame einfach eine neue URL aufrufen und dann per JavaScript auf die Inhalte zugreifen. Diese Inhalte lassen sich dann in anderen Frames weiterverwerten. Es gibt allerdings zwei Nachteile: ■ ■
Der Ansatz ist aufwändiger als das Absetzen eines Ajax-Requests. Sie benötigen einen Frame, den der Nutzer nicht sieht und der ihn in der Bedienung der Seite unter Umständen verwirrt oder stört (z.B. beim Bookmarksetzen).
14
Zieht man die gute Unterstützung für das XMLHttpRequest noch mit ins Kalkül, bleibt für die Praxis klassisches Ajax die deutlich bessere Alternative.
15
Versteckte Frames sind auch ein möglicher Ersatz für Cookies. Sie können im versteckten Frame versteckte Textfelder () einfügen und dort Daten zwischenspeichern.
16 TIPP
17 19.2.3 Frames füllen
18
Bisher enthielt jeder Frame eine eigene HTML-Datei. Dies ist jedoch nicht zwingend erforderlich. Sie können einen Frame auch ausschließlich per Skript füllen.
19
Das Füllen erfolgt am einfachsten mit einer leeren Seite:
20
Wollen Sie dagegen mit JavaScript Inhalte in den Frame schreiben, verwenden Sie am besten eine JavaScript-URL mit javascript:. Dazu sind folgende Schritte notwendig: 1.
21
Die Funktion wird im Frame aufgerufen. Da das Skript im Frameset steht, erfolgt die Adressierung mit parent. src="javascript:parent.fuellen('Neuer Inhaltsframe')"
22
2.
Als Parameter übergibt der Funktionsaufruf einen Text, der in die neue Seite eingefügt werden soll. 3. Das Skript enthält das HTML-Grundgerüst und übernimmt den Parameter. 4. Abschließend gibt es mit return die HTML-Seite zurück.
23 24
Hier der vollständige Code: Listing 19.14: Einen Frame mit JavaScript füllen (frame_fuellen.html) Frameset
Abbildung 19.12: Der rechte Frame wurde mit JavaScript gefüllt.
19.2.4 History und Frames Die History lässt sich auch über Frame-Grenzen hinweg steuern. Die häufigste Anwendung besteht darin, in einem Frame die Befehle für die History eines anderen – meist des Inhaltsframes – bereitzuhalten. Mit folgender Zeile gehen Sie aus einem anderen Frame im Inhaltsframe um einen Schritt in der History zurück: parent.inhalt.history.back();
In Abschnitt 16.1 »history-Objekt« erfahren Sie mehr über den Umgang mit der History in JavaScript. REF
546
Frames erzwingen und vermeiden
19.3 Frames erzwingen und vermeiden Der Nutzer hat im Browser die Möglichkeit, einen Frame in einem neuen Fenster zu öffnen. Eine eventuell vorhandene Navigationsleiste oder ein Frame mit Werbebanner verschwinden in diesem Fall. Wenn dies nicht gewünscht wird, können Sie in dem Frame, der immer im Frameset enthalten sein soll, folgenden Code bei onload im -Tag aufrufen:
14
if(self == top) { top.location.href = "frameset.html"; }
15
Der Code prüft, ob der aktuelle Frame an oberster Stelle steht. Wenn dies der Fall ist, wird das Frameset stattdessen geladen. Bei der Anzeige gibt es eine kurze Verzögerung. Das heißt, der Nutzer sieht zunächst nur den Frame, bevor das Frameset geladen wird. Die Dauer der Ladezeit ist von der Geschwindigkeit der Internetverbindung abhängig.
16 17
INFO
Der umgekehrte Fall ist genauso ärgerlich. Ihre Seite wird immer im Frameset einer anderen fremden Seite geladen. Abmahnen wäre eine Lösung. Noch eleganter ist folgender Code, der in Ihre Seite bei onload eingebaut werden sollte:
18 19
if(self != top) { top.location.href = self.location.href; }
20
Sollte die aktuelle Seite nicht die oberste in der Hierarchie sein, wird sie als oberste Seite neu geladen.
21 19.4 iFrames
22
Ein iFrame oder auch Floating Frame (fließender Frame) ist ein Dokument im Dokument. Er teilt im Gegensatz zum herkömmlichen Frame das Browserfenster nicht in einzelne Unterfenster auf, sondern ist direkt in ein HTMLDokument integriert. iFrames fließen entweder im HTML-Fluss mit oder können per Stylesheet-Anweisung absolut positioniert werden.
23
iFrames sind in allen neueren Browsern integriert. Besonders häufig werden sie eingesetzt, um auf einer Website Inhalte aus anderen Websites einzufügen. Sie dienen damit als so genannte Wrapper. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
24 Tabelle 19.9: iFrames
547
Frames
Das folgende Beispiel erstellt einen iFrame mit einer Größe von 300 * 100 Pixeln: Listing 19.15: Ein einfacher iFrame (iframe.html) iFrames iFrames Hier steht der Ersatztext, für Browser die keine iFrames anzeigen können. Kommentar neben dem iFrame
Abbildung 19.13: Ein einfacher iFrame
19.4.1 JavaScript-Zugriff Der Zugriff auf und aus iFrames erfolgt wie bei herkömmlichen Frames. Der iFrame wird ebenfalls Teil der Kollektion frames[] und kann über den Index window.frames[0]
oder über den Namen window.frames["i1"]
angesprochen werden. Die dritte Alternative ist der direkte Zugriff über den Namen: window.i1
548
iFrames
Das folgende Beispiel ändert die Hintergrundfarbe des Dokuments im iFrame. Der Nutzer kann dazu im Textfeld einen Farbnamen oder den hexadezimalen Farbwert eingeben. Das Skript besteht aus folgenden Einzelschritten: 1. Ein Anklicken der Schaltfläche ruft die Funktion farbe() auf. 2. In der Funktion wird als Erstes geprüft, ob das Textfeld nicht leer ist. 3. Ist dies der Fall, ist also eine Farbe eingetragen, erhält die Variable iframe den Zugriff auf den iFrame:
14 15
var iframe = window.i1;
4. Anschließend wird die Hintergrundfarbe des iFrames auf den Wert des Texteingabefelds gesetzt.9
16
iframe.document.body.style.backgroundColor = document.formular.eingabe.value;
Hier der vollständige Code:
17
Listing 19.16: Die Farbe des iFrames wechseln (iframe_zugriff.html) Farbe des iFrames ändern Farbe des iFrames ändern Hier steht der Ersatztext, für Browser die keine iFrames anzeigen können. Farbname
9
18 19 20 21 22 23 24
Wie Sie mit Stilen arbeiten, erfahren Sie in Kapitel 22 »CSS und JavaScript«.
549
Frames
Abbildung 19.14: Der Nutzer gibt eine Farbe ein und klickt auf die Schaltfläche (links); das iFrame erhält daraufhin eine andere Hintergrundfarbe (rechts).
19.4.2 iFrame-Objekt Der klassische Zugriff auf iFrames ist nicht die einzige Lösung. Das -Tag stellt für JavaScript auch ein Browserobjekt dar, auf das der direkte Zugriff über den W3C-konformen Weg (document.getElementById) und für den Internet Explorer 4 über document.all möglich ist. Dies setzt jedoch voraus, dass Sie dem iFrame zuvor eine ID zugewiesen haben. Die Eigenschaften des iFrame-Objekts gleichen weitgehend denen des Frame-Objekts (siehe Abschnitt »Eigenschaften für « unter Punkt 19.1.2). Die folgende Tabelle gibt eine Übersicht über die wichtigsten Eigenschaften. Die meisten entsprechen den HTML-Attributen für iFrames. Tabelle 19.10: Wichtige Eigenschaften des iFrame-Objekts
550
Eigenschaft
Beschreibung
align
Gibt die Ausrichtung des iFrames relativ zum vorhandenen Platz im HTML-Fluss an. Möglich sind left (linksbündig), center (zentriert) oder right (rechtsbündig).
allowTransparency
Gibt als Wahrheitswert an, ob Transparenz erlaubt ist (true) oder nicht (false).
frameBorder
Erlaubt Rahmen (»yes« bzw. 1) oder verbietet (»no« bzw. 0) sie.
hspace
Horizontaler Abstand zwischen iFrame-Rahmen und Inhalt.
vspace
Vertikaler Abstand zwischen iFrame-Rahmen und Inhalt.
marginHeight
Abstand zwischen Inhalt und iFrame-Rahmen in der Vertikalen.
marginWidth
Abstand zwischen Inhalt und iFrame-Rahmen in der Horizontalen.
scrolling
Erlaubt (»yes« bzw. 1) oder verbietet (»no« bzw. 0) Scrolling. auto lässt den Browser entscheiden.
src
Ändert die Quelle des iFrames. Dies erlaubt Ihnen, das Dokument im iFrame zu ändern.
iFrames
NS4.x
align
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
allowTransparency
hspace
vspace
frameBorder
marginHeight
marginWidth
scrolling
src
Tabelle 19.11: Wichtige Eigenschaften des iFrame-Objekts
14 15 16 17
19.4.3 Praxiseinsatz und Ajax
18
Die iFrames werden seit den letzten zwei Jahren häufiger im Web eingesetzt. Vor allem zum Einbinden von externem Inhalt eignen sie sich sehr gut, da Sie nur eine URL angeben müssen, sich aber nicht um die HTMLFormatierung oder das Parsen kümmern müssen.
19
Insofern kann ein iFrame auch eine Alternative zu einem Ajax-Aufruf sein. Sinnvoll ist das meist aber nur, wenn der Inhalt im iFrame direkt ausgegeben wird. Der Ansatz eines versteckten iFrames ist ähnlich problematisch wie der oben schon beschriebene versteckte Frame.
20 21
Und der iFrame hat noch einen weiteren Nachteil: Er ist immer ein Fremdkörper in der Seite. Das bedeutet zum Beispiel, dass der Inhalt eines iFrames von Suchmaschinen als nicht vorhanden bzw. nicht als Bestandteil der Webseite wahrgenommen wird. Sind darin aber die Hauptinhalte einer Seite enthalten, führt das zwangsläufig zu schlechterem Ranking.
22 23 24
551
Inhalt
20 Formulare 14
15
Ursprünglich waren Webformulare eines der wichtigsten und wenigen Einsatzgebiete von JavaScript. Das hat sich geändert, denn andere Anwendungsmöglichkeiten – wie dynamische Effekte und Ajax – kamen hinzu. Allerdings wird JavaScript insbesondere für die Vollständigkeitsüberprüfung und den direkten Zugriff auf Formularelemente immer noch sehr häufig eingesetzt.
16
17
JavaScript tritt hier nicht oder nur wenig in Konkurrenz mit serverseitigen Programmiersprachen wie PHP und ASP.NET, sondern ergänzt sie eher. Dies wird noch eingehender in Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke« diskutiert. Als Erstes lernen Sie jedoch den Zugriff auf die verschiedenen Formularelemente in HTML und die jeweiligen Besonderheiten kennen.
20.1
18
19
Zugriff
20
Den Zugriff auf Formulare und ihre Elemente verwendeten bereits einige Beispiele in den vorangegangenen Kapiteln dieses Buches. Er ist einfach und in allen wichtigen und JavaScript-fähigen Browsern integriert. Zuerst erfolgt der Zugriff auf das Formular: ■
■
21
Die am häufigsten eingesetzte Variante ist der Zugriff mit dem nameAttribut. Hier wird dem Formular ein Name gegeben, beispielsweise Formularname. Damit kann das Formular dann angesprochen werden:
22
document.Formularname
23
Die zweite Alternative ist die Kollektion forms[Index]. Sie enthält alle Formulare einer Seite. Die Formulare werden nach ihrem Auftauchen beginnend bei 0 mit einem Index versehen. Problematisch ist hier allerdings eine spätere Änderung der Reihenfolge der Formulare.
24
document.forms[0]
Statt des Index können Sie auch in der forms-Kollektion den Namen des Formulars verwenden. Der direkte Zugriff ist allerdings kürzer und übersichtlicher.
INFO
553
ndex
Die Formularelemente können ebenfalls auf verschiedene Arten angesprochen werden:
Formulare
■
direkt mit ihrem Namen (name-Attribut): document.Formularname.Elementname
■
über die Kollektion elements[Index], die alle Elemente eines Formulars enthält. Diese Zeile greift beispielsweise auf das erste Element im ersten Formular zurück: document.forms[0].elements[0]
INFO
Auch auf Formularelemente können Sie über elements["Name"] zugreifen. Dies ist beispielsweise sinnvoll, wenn der Name des Formularelements in einer Variablen gespeichert wird. Dann erfolgt der Zugriff über elements[Variable].
Tabelle 20.1: Formularzugriff
TIPP
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Hat ein Formularelement eine ID, kann der Zugriff natürlich auch direkt über die ID per getElementById() erfolgen. Dies geht nur in älteren Browsern nicht, ist aber gerade in Ajax-Anwendungen, die sowieso Netscape 4.x und Internet Explorer 4 ausschließen, kein Problem.
20.1.1 Zugriff mit name-Attribut Das folgende Beispiel illustriert den Zugriff über das name-Attribut: Der Wert eines Textfelds wird mit dem value-Attribut nach Anklicken der Schaltfläche ausgelesen. Der Zugriff auf das Textfeld erfolgt nach dem Schema: document.Formularname.Elementname.value
Hier der vollständige Code: Listing 20.1:
Formularzugriff über das name-Attribut (zugriff_name.html)
Formularzugriff mit name-Attributen
554
Zugriff
Abbildung 20.1: Die Ausgabe greift auf das Textfeld zu.
14 15 16 17 18 20.1.2 Zugriff mit forms-Kollektion
19
Das Beispiel aus Listing 20.1 ist schnell umgeschrieben, um auch mit der forms-Kollektion zu funktionieren. Da der Zugriff über elements[Index] schnell sehr schwer zu verwalten ist, weil sich die Reihenfolge von Elementen verändern kann, wird er in der Praxis nur dazu verwendet, die Formularelemente per Schleife auszulesen. Daher können Sie, wie im folgenden Beispiel geschehen, auch den Zugriff per Kollektion und den Zugriff per Name mischen: Listing 20.2:
20 21
Zugriff auf Formularelement per forms-Kollektion (zugriff_kollektion.html)
Formularzugriff mit forms-Kollektion
22 23 24
Das Ergebnis dieses Codes ist dasselbe wie in Abbildung 20.1.
555
Formulare
20.2 Formularelemente HTML bietet Ihnen verschiedene Formularelemente, auf die Sie alle mit JavaScript zugreifen können. Die folgenden Abschnitte zeigen Ihnen, was es bei den einzelnen Elementen zu beachten gibt. Die hier vorgestellten Formularelemente stehen in HTML bereits in den ersten Browsern Netscape Navigator 2 und Internet Explorer 3 in der ersten Version zur Verfügung. Tabelle 20.2: Formularelemente
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
20.2.1 Textfeld Textfelder sind vielleicht die wichtigsten Formularelemente, denn sie erlauben die Eingabe von beliebigen Werten durch den Nutzer.
Der Zugriff auf den Inhalt des Textfelds erfolgt über die Eigenschaft value. Mit ihr lesen und setzen Sie den Wert des Textfelds. Besitzt das Textfeld ein HTML-Attribut value, gibt dies eine Vorausfüllung für das Textfeld an. Passwortfeld
Das Passwortfeld ist auch ein Textfeld. Die eingegebenen Buchstaben werden jedoch auf der Webseite mit Sternchen (*)1 verdeckt, damit niemand bei der Passworteingabe mitlesen kann. Passwortfelder werden genauso ausgelesen wie normale Textfelder. Das Passwort findet sich als normaler Wert im value-Attribut. Beachten Sie, dass Passwortabfragen mit JavaScript nicht sicher sind. Der Nutzer hat Zugriff auf den Quellcode des Skripts, auch wenn dieser als externe Datei gespeichert ist. Dort steht auch irgendwo das Passwort. Sie sehen dies an der folgenden beispielhaft dargestellten Prüffunktion, die Sie so oder ähnlich immer noch häufiger im Web finden: var pw = "JavaScript"; function pruefen() { if (document.formular.pass.value == pw) { document.write("Herzlich willkommen!"); } else { alert("Nicht autorisiert!"); } } 1
556
Der englische Begriff lautet Asterisk.
Formularelemente
Versteckte Textfelder
Abbildung 20.2: Der Nutzer gibt das Passwort ein (links) und die JavaScript-Überprüfung ergibt, dass es richtig ist. Allerdings kann der Nutzer das Passwort bereits im Quellcode auslesen!
14 15
16
Die zweite Art Textfeld ist das versteckte Textfeld. Es ist für den Nutzer nicht sichtbar, sondern nur im Quelltext und damit im DOM (Document Object Model) für den JavaScript-Programmierer vorhanden. Das wichtigste Einsatzgebiet von versteckten Textfeldern ist die Speicherung von Werten.
17
Das folgende Beispiel liest den Inhalt eines Textfelds ein und übergibt ihn mit einem Trennzeichen versehen2 an ein verstecktes Textfeld. Wurden drei Werte eingegeben, liest das Skript das versteckte Formularfeld aus, trennt die drei Werte daraus mit der String-Methode split("Trennzeichen") und gibt sie nacheinander aus: Listing 20.3:
18 19
Werte in einem versteckten Formularfeld speichern (hidden.html)
Verstecktes Textfeld 2
20 21 22 23 24
Wählen Sie als Trennzeichen ein Zeichen, das der Nutzer nicht in seinem Text verwendet bzw. untersagen Sie ihm die Verwendung. Das Fragezeichen (?) ist eine zufällige Wahl und oft nicht die beste, falls der Nutzer auch Fragen eingeben darf. Andere gute Trennzeichen sind \t und \n, da der Nutzer sie nicht in das Textfeld eintragen kann.
557
Formulare
Geben Sie nacheinander drei Werte ein!
Abbildung 20.3: Der Nutzer kann drei Werte eingeben, …
Abbildung 20.4: … und dann werden die Werte untereinander ausgegeben.
TIPP
Die hier gezeigte Anwendung ist natürlich nur ein einfaches Beispiel. Versteckte Textfelder können allerdings in Verbindung mit Cookies oder einer Navigation über Frames noch in wesentlich komplexeren Anwendungen eingesetzt werden. Beispielsweise können Sie in versteckten Textfeldern, die in einem unsichtbaren, weil sehr kleinen Frame liegen, Daten über mehrere Webseiten hinweg speichern. onchange Das onchange-Ereignis tritt ein, wenn der Nutzer in einem Textfeld eine Veränderung vornimmt. Das ist nicht die Eingabe jedes Buchstabens oder Zei-
558
Formularelemente
chens, sondern tritt erst ein, wenn der Eingabecursor aus dem Textfeld verschwindet. Dies geschieht normalerweise, wenn der Nutzer ein anderes Formularelement anwählt oder auf einen anderen Bereich der Seite klickt. Im folgenden Skript wird bei onchange der Text des Formularfelds ausgegeben: Listing 20.4:
14
Mit onchange Änderungen abfangen (onchange.html)
onchange
15 16 17 18 Abbildung 20.5: Wenn sich der Text ändert, wird er ausgegeben.
19 20 21 22 23 24
Betätigt der Nutzer die (Enter)-Taste und das Formular hat nur ein Textfeld, wird dies als Klick auf die Schaltfläche SUBMIT, also als Übertragung des Formulars angesehen. Dies ist auch der Fall, wenn keine SUBMIT-Schaltfläche vorhanden und keine URL für die Übertragung angegeben ist (action-Attribut, siehe Abschnitt »Submit« unter Punkt 20.2.7). Um den Tastendruck auf (Enter) kontrolliert zu berücksichtigen, verwenden Sie die zugehörigen Ereignisse wie beispielsweise onkeydown (vergleiche Kapitel 9 »Ereignisse und Event-Handler«).
INFO
559
Formulare
Auswählen Für das Auswählen von Text in einem Textfeld gibt es eine Methode und ein Ereignis. ■ ■
Die Methode select() wählt den gesamten Text des Textfelds aus. Das Ereignis onselect tritt ein, wenn der Nutzer einen Teil des Textes im Textfeld auswählt.
Tabelle 20.3: Auswählen select() onselect
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel zeigt Ereignis und Methode im Zusammenspiel. Das Skript wählt mit select() den ganzen Text im Textfeld aus, sobald der Nutzer einen Teil davon auswählt: Listing 20.5:
Text auswählen (select.html)
onselect
Abbildung 20.6: Wählt der Nutzer einen Buchstaben, wird der gesamte Text ausgewählt.
560
Formularelemente
Um die Auswahl von Text im Textfeld wieder aufzuheben, müssen Sie dem Feld mit der Methode blur() den Fokus nehmen. TIPP
Browserbesonderheiten Der Internet Explorer ab Version 4 bietet ein selection-Objekt. Mit der Methode createRange() dieses Objekts können Sie ein TextRange-Objekt aus der Auswahl erzeugen und es beispielsweise in ein anderes Textfeld kopieren. Der Netscape Navigator unterstützt in Version 4 die Methode document.getSelection(). Im Mozilla ist ein selection-Objekt hinzugekommen, das allerdings nicht mit den Methoden des Internet Explorers arbeitet und auch nicht für Formularelemente funktioniert.
14 15 16
Art des Textfelds Die Eigenschaft type liefert die Art des Textfelds: text, password oder hidden. So können Sie beispielsweise die versteckten Textfelder identifizieren.
type
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
17 Tabelle 20.4: Die Eigenschaft type
18
Sonstiges
19
Auch bei Textfeldern ist natürlich die Optik wichtig. Dafür gibt es zwei Eigenschaften:
20
■ ■
maxLength – liest und verändert die maximal mögliche Zahl an Zeichen eines Textfelds. size – liefert und ändert die Größe des Textfelds in Zeichen. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
maxLength
size
21 Tabelle 20.5: Die Eigenschaften maxLength und size
23
Eine weitere Eigenschaft – readOnly – sorgt dafür, dass ein Textfeld nicht gelesen werden kann. Sie gleicht in der Wirkung disabled3, ohne dass allerdings der Feldhintergrund grau wird. Außerdem bleibt das Feld veränderbar. NS4.x
readOnly
3
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
22
24 Tabelle 20.6: Die Eigenschaft readOnly
Mehr Informationen zu diesem Thema erhalten Sie in Abschnitt 16.5.1 »Laufschrift schützen«.
561
Formulare
WWW
Unter dem Namen size_maxLength_readOnly.html finden Sie im Ordner code\kap20 ein Beispiel für die drei Eigenschaften im Download-Archiv (siehe Abbildung 20.7).
Abbildung 20.7: Die drei Eigenschaften können über zwei Schaltflächen geändert werden.
20.2.2 Mehrzeilige Textfelder
Mehrzeilige Textfelder4 haben gegenüber normalen Textfeldern den Vorteil, dass sie aus mehreren Zeilen bestehen. Die Breite eines mehrzeiligen Textfelds wird in Spalten angegeben. Eine Spalte entspricht dabei einem Zeichen. Beide können über die Eigenschaften rows (Zeilen) und cols (Spalten) geändert werden. Die übrigen Möglichkeiten für mehrzeilige Textfelder entsprechen denen der einzeiligen Textfelder. Die Abfrage oder Festlegung des Werts erfolgt also beispielsweise über die value-Eigenschaft. document.Formularname.Feldname.value
Tabelle 20.7: Die Eigenschaften cols und rows
NS4.x
cols und rows
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel ändert die Zahl der Zeilen und Spalten, wenn der Nutzer auf eine Schaltfläche klickt: Listing 20.6:
Zeilen und Spalten eines mehrzeiligen Textfelds ändern (textarea.html)
Eigenschaften von mehrzeiligen Textfeldern Vorgabewert
14 15 Abbildung 20.8: Klein und groß
16 17 18 19 20
20.2.3 Checkbox
21
Checkboxen sind die kleinen Kästchen, die der Nutzer anklickt, um diese Option auszuwählen. Dementsprechend heißen sie auch Optionen oder Optionskästchen.5
22
Der Zugriff erfolgt über den Namen. Mit der Eigenschaft checked stellen Sie fest, ob die Checkbox zurzeit aktiviert ist oder nicht. Diese Eigenschaft liefert einen Wahrheitswert, kann allerdings auch per JavaScript geändert werden. NS4.x
checked
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
23 Tabelle 20.8: checked
Die Eigenschaft steht auch bereits in Uraltbrowsern wie Netscape 2 und Internet Explorer 3 in der ersten Version zur Verfügung.
5
Da Ihnen in der Programmierung meist der englische Begriff begegnen wird, finden Sie ihn auch in diesem Buch.
563
24
Formulare
Das Beispiel hierzu überprüft, ob die Checkbox aktuell angewählt ist oder nicht, und gibt entsprechend eine Meldung aus: Listing 20.7:
Mit einer Checkbox arbeiten (checkbox.html)
Checkboxen
Zum Aufrufen der Funktion wird im Beispiel aus Listing 20.7 das onclickEreignis der Checkbox verwendet. Alternativ können Sie natürlich auch eine Schaltfläche anklicken oder den Formularversand6 verwenden. Abbildung 20.9: Die Checkbox wurde angewählt.
6
564
Grundsätzliches zur Schaltfläche SUBMIT erfahren Sie im Abschnitt »Submit« weiter unten.
Formularelemente
Die Eigenschaft defaultChecked überprüft, ob die Checkbox standardmäßig angeklickt ist (true), also das HTML-Attribut checked besitzt. Neben dieser Eigenschaft gibt es noch einige Eigenschaften, die ebenfalls Textfelder besitzen: type liefert beispielsweise den Typ, also checkbox, name enthält den Namen der Checkbox und value kann einen Wert enthalten, den serverseitige Skripte nutzen können.
14 NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
In verschiedenen serverseitigen Programmiersprachen werden in der Regel auch für Checkboxen (wie für Radiobuttons) gleiche Namen vergeben. Die Checkboxen stehen dann in einem Array zur Verfügung. In manchen Sprachen, beispielsweise PHP, müssen in diesem Fall nach dem Namen auch eckige Klammern erscheinen.
Tabelle 20.9: Weitere Eigenschaften von Checkboxen
16 TIPP
17 18
20.2.4 Radiobutton Radiobuttons sind ein wenig schwieriger zu behandeln als Checkboxen, da sie immer in Rudeln auftreten. Radiobuttons, die denselben Namen haben, bilden eine Gruppe. Aus dieser Gruppe darf per Definition nur eine der Optionen ausgewählt sein.
19 20
Wenn das name-Attribut gleich ist, bleibt die Frage, wodurch die einzelnen Optionen voneinander unterschieden werden können. Eine Möglichkeit ist das value-Attribut. Jeder Radiobutton erhält einen eigenen Wert.7
21
value gibt es bereits in den ersten Browsern mit JavaScript-Unterstützung. NS4.x
value
15
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 20.10: value-Eigenschaft
22 23
Eine der Hauptanwendungen von Radiobuttons in Formularen besteht darin, den Nutzer aus mehreren Alternativen eine auswählen zu lassen. Das Paradebeispiel – nämlich das Geschlecht des Nutzers – wird auch im folgenden Skript verwendet.
24
Das Skript übermittelt als Parameter mit dem Schlüsselwort this die Referenz auf den soeben angeklickten Radiobutton. this spielt bei Formularen eine große Rolle, da es einfach im Ereignis an eine Funktion übergeben werden kann und Sie sich die umständliche Adressierung des Formularelements über document.Formularname.Elementname sparen. 7
Eine Alternative wäre die Verwendung des id-Attributs. Dies ist allerdings in der Praxis weniger gebräuchlich.
565
Formulare
Listing 20.8:
Mit Radiobuttons arbeiten (radiobuttons.html)
Radiobuttons weiblich männlich
Abbildung 20.10: Dieser Nutzer ist männlich.
REF
566
In Kapitel 15 »Bilder« finden Sie ein Beispiel, wie Sie statt eines RadiobuttonFormularelements auch eine Grafik mit ähnlicher Funktionsweise programmieren können.
Formularelemente
20.2.5 Auswahlliste Eine Auswahlliste8 besteht aus mehreren Optionen, aus denen der Nutzer eine oder mehrere auswählen kann. Dieses Formularelement ist vielleicht das komplizierteste, bietet dafür aber auch sehr viele Möglichkeiten. Prinzipiell gilt es zwei Arten von Auswahllisten zu unterscheiden: ■ ■
14
Auswahllisten, bei denen nur eine Option auswählbar ist. Auswahllisten, die das Auswählen von mehreren Optionen erlauben. Diese Auswahllisten besitzen im -Tag das Attribut multiple. Sie finden sie im Abschnitt »Mehrere Optionen auswählen« erläutert.
15
Zugriff auf Auswahllisten
16
Der Zugriff auf eine Auswahlliste erfolgt natürlich bei einem Ereignis. Dies kann das Anklicken einer Schaltfläche im Formular oder auch das Versenden des Formulars sein. Hierbei handelt es sich jedoch um von außen angestoßene Ereignisse. Das sinnvollste Ereignis in der Auswahlliste selbst ist onchange. Es tritt ein, wenn der Nutzer die Option ändert, und steht für Auswahllisten bereits in Uraltbrowsern ab Netscape 2 und Internet Explorer 3 zur Verfügung.
17 18
Option 1 Option 2 Option 3
19 20
Dieses Beispiel ruft eine Funktion auf. Natürlich wäre es auch möglich, den Code direkt in das onchange-Ereignis zu schreiben. Dies geschieht sehr oft, wenn Auswahllisten als Navigationselement verwendet werden.9
21
Der Zugriff auf die Auswahlliste erfolgt nun genau wie bei anderen Formularelementen entweder über die Kollektionen forms[] und elements[] oder über die Namen.
22
var liste = document.formular.auswahl;
23
Um festzustellen, welche Option ausgewählt ist, hilft die Eigenschaft selectedIndex. Sie enthält den Index (beginnend bei 0) des ausgewählten Optionselements.
24
var index = liste.selectedIndex;
In der Kollektion options[Index] werden alle Optionen einer Auswahlliste gespeichert. Mit der Eigenschaft text ist der Text im jeweiligen Tag abrufbar: alert(index + ": " + liste.options[index].text);
8 9
Die Auswahlliste wird – nach dem HTML-Tag – auch Select-Element oder Select-Objekt genannt. Siehe Abschnitt »Auswahlliste als Navigationselement« weiter unten.
567
Formulare
Das folgende Skript gibt den Index und den Text der ausgewählten Option aus: Listing 20.9:
Zugriff auf eine Auswahlliste (auswahlliste.html)
Auswahlliste Option 1 Option 2 Option 3
Abbildung 20.11: Die Warnmeldung gibt Index und Text der Option aus.
Die hier verwendeten und einige andere wichtige Eigenschaften für Auswahllisten können Sie der folgenden Tabelle 20.11 entnehmen. Darüber hinaus finden Sie in Tabelle 20.12 eine Übersicht über die Browserkompatibilität.
568
Formularelemente
Eigenschaft
Beschreibung
options[Index]
Kollektion (Array) aus allen Optionen einer Auswahlliste. Ein Element wird mit der Indexnummer aufgerufen. Name oder ID funktionieren nicht.
defaultSelected
Ergibt true, wenn die Option standardmäßig ausgewählt ist. Kann nur gelesen, nicht gesetzt werden. Die Einstellung wird mit dem HTML-Attribut selected im -Tag vorgenommen.
index
Der Index eines Elements. Er kann nur ausgelesen werden. Dies ist meist nicht sehr sinnvoll, da Elemente vorher mit Index angesprochen werden müssen.
selectedIndex
Der Index der ausgewählten Option. Er kann auch geändert werden. In Auswahllisten, bei denen mehrere Optionen ausgewählt werden können, enthält er den Index der ersten ausgewählten Option. Ist kein Element ausgewählt, entspricht selectedIndex -1.
size
Gibt an, wie viele Zeilen der Auswahlliste sichtbar sind. Eine einzeilige Auswahlliste heißt auch Pulldown-Menü.
text
Der Text innerhalb der -Tags. Er kann auch geändert werden.
value
Der Wert, der im value-Attribut eines -Tags angegeben wurde. Er ist auch änderbar. Der Zugriff erfolgt wie bei text über die options-Kollektion.
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
options
defaultSelected
index
selectedIndex
size text
value
Die drei Eigenschaften defaultSelected, text und value stehen für das -Objekt10 und das -Objekt zur Verfügung. Dass dies programmiertechnisch nicht dasselbe ist, werden Sie im Abschnitt »Auswahllisten bearbeiten« weiter unten sehen, in dem Methoden vorgestellt werden, die in verschiedenen Browsern unterschiedlichen Objekten zugeordnet werden.
Tabelle 20.11: Wichtige Eigenschaften für Auswahllisten
14 15 16 17 18 Tabelle 20.12: Wichtige Eigenschaften für Auswahllisten (Browserkompatibilität)
19 20 21 22 23 24
HALT
10 Zugriff über options-Kollektion.
569
Formulare
Mehrere Optionen auswählen Damit der Nutzer mehrere Optionen auswählen kann, müssen Sie das HTML-Attribut multiple in das -Tag schreiben. Die gleichnamige Eigenschaft multiple erlaubt Ihnen in neueren Browsern, per JavaScript eine Auswahlliste von einer auf mehrere Optionen und zurück umzuschalten. Das folgende Skript liest alle ausgewählten Optionen einer Auswahlliste aus und bietet zusätzlich eine Schaltfläche, um multiple umzuschalten. Das Auslesen erfolgt mit einer Schleife, die mit length die Zahl der Elemente der Auswahlliste aufnimmt und über selected für jede Option prüft, ob sie ausgewählt ist (true) oder nicht (false). Listing 20.10: Eine Auswahlliste mit mehreren Optionen auslesen (auswahlliste_multiple.html) Auswahlliste mit mehreren Optionen Option 1 Option 2 Option 3
570
Formularelemente
Abbildung 20.12: Die Auswahlliste im Original
14 15 16 17 Abbildung 20.13: Ein Klick auf die Schaltfläche AUSGEBEN ruft die gleichnamige Funktion auf und gibt Index und Text der ausgewählten Optionen aus.
18 19 20 21 22
Der Netscape Navigator 4 unterstützt multiple nicht. Sie können hier die Art der Auswahlliste an der type-Eigenschaft erkennen. Der type ist bei Mehrfachauswahl select-multiple und bei Einfachauswahl select-one.
length
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
multiple selected
23 Tabelle 20.13: Weitere Eigenschaften für Auswahllisten (Browserkompatibilität)
571
24
Formulare
Abbildung 20.14: Ein Klick auf die Schaltfläche MEHRERE? erlaubt dem Nutzer, nur noch eine Option auszuwählen.
Auswahllisten bearbeiten Das Bearbeiten von Auswahllisten ist nicht ganz so unkompliziert wie das Abfragen. Sie haben mehrere Möglichkeiten: ■
Sie können den Text einer bestehenden Option mit der Eigenschaft text ändern. document.formular.auswahl.options[0].text = "Markt+Technik"
■
Sie ändern den Wert einer bestehenden Option mit value. document.formular.auswahl.options[0].value = "http://www.mut.de"
■
Sie löschen eine Option. Dazu setzen Sie die Option auf null. Die Zahl der Optionen, also die length-Eigenschaft der Auswahlliste, verringert sich um 1. document.formular.auswahl.options[0] = null;
■
Sie erstellen ein neues Option-Objekt und fügen es hinzu (Beispiel siehe Listing 20.11). Das Objekt erhält als Parameter den Text11 und optional einen Wert.12 Ebenfalls optional ist der Standardstatus, der angibt, ob die Option standardmäßig aktiviert (selected) ist (true) oder nicht (false). Die vierte Option gibt den aktuellen Status an: aktiviert ist true, nicht aktiviert false. var obj = new Option("Text", "Wert", Standardstatus, Status); liste.options[Index] = obj;
■
Sie verwenden die Methoden add() und remove(), um Elemente hinzuzufügen oder zu entfernen.
11 Entspricht der text-Eigenschaft. 12 Entspricht der value-Eigenschaft.
572
Formularelemente
Option-Objekt hinzufügen und löschen Das folgende Beispiel erstellt beim Anklicken der Schaltfläche HINZUFÜGEN eine neue Option, die als Text den Wert eines Textfelds erhält. Die neue Option wird an das Ende der Auswahlliste angefügt. Eine zweite Schaltfläche dient dazu, die aktuell ausgewählte Option zu entfernen. Die Funktion weg() setzt die Option mit dem Index auf null. Listing 20.11:
14
Ein neues Option-Objekt hinzufügen und eine Option löschen (option_objekt.html)
Einer Auswahlliste ein Objekt hinzufügen und löschen Option 1 Option 2 Option 3
15 16 17 18 19 20 21 22 23 Abbildung 20.15: Eine neue Option hinzufügen (links): Sie erscheint am Ende der Liste (rechts).
573
24
Formulare
Abbildung 20.16: Ein Objekt auswählen (links) und wieder entfernen (rechts)
Wenn Sie ein neues Objekt einer bestehenden Option zuweisen, wird diese Option ersetzt. Die folgende Fallunterscheidung wandelt die Funktion zum Hinzufügen aus Listing 20.11 so ab, dass – falls ein Objekt ausgewählt ist – das ausgewählte Objekt durch das neue Objekt ersetzt wird. Ist dies nicht der Fall, wird das neue Objekt am Ende angehängt. Listing 20.12:
Eine bestehende Option ersetzen (option_objekt_ersetzen.html)
if (liste.selectedIndex != -1) liste.options[liste.selectedIndex] = obj; else liste.options[liste.length] = obj; }
Abbildung 20.17: Die neue Option (links) ersetzt die ausgewählte (rechts).
Wollen Sie die bestehende Option nicht ersetzen, sondern die neue Option dazwischen einfügen, müssen Sie alle nachfolgenden Optionen verschieben. Dazu dient eine einfache for-Schleife. Listing 20.13: Ein neues Objekt einfügen (option_objekt_einfuegen.html) function einfuegen() { var liste = document.formular.auswahl; var obj = new Option(document.formular.eingabe.value, "Neue Option"); if (liste.selectedIndex != -1) { for (var i = liste.length; i>liste.selectedIndex;i--) {
574
Formularelemente
liste.options[i] = new Option(liste.options[i-1].text); } liste.options[liste.selectedIndex] = obj; } else { liste.options[liste.length] = obj; } }
14 Abbildung 20.18: Eine neue Option soll vor Option 2 eingefügt werden (links). Rechts sehen Sie das Ergebnis.
15 16 17 18
Option
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 20.14: Option-Objekt: Löschen und Einfügen von neuen Optionen
add() und remove() Das Einfügen von neuen Objekten ist mit dem im letzten Absatz beschriebenen Vorgehen recht arbeitsaufwändig. Daher gibt es in neueren Browsern die add(Objekt, Index)-Methode. Sie erlaubt es Ihnen, ein Objekt an einen beliebigen Index zu schieben. Die nachfolgenden Optionen werden verschoben. Zum Löschen einer Option bietet sich remove(Index) an. Auch hier ist die Angabe des Index ausreichend.
zu Select-Objekt zu Option-Objekt
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
20 21 22
Das Problem bei beiden Methoden: Für den Netscape Navigator 6 und höher sowie Mozilla gehören die Methoden zum Select-Objekt, für den Internet Explorer und Opera dagegen zum Option-Objekt oder zum Select-Objekt.13 Um auf Nummer sicher zu gehen, sollten Sie immer eine Fallunterscheidung verwenden, wie sie in Listing 20.14 gezeigt wird. NS4.x
19
23 24 Tabelle 20.15: add() und remove() – unterschieden nach Zugehörigkeit
13 Manche Bücher oder Internetquellen sprechen hier immer noch von einem Bug, entweder des Internet Explorers oder des Netscape Navigators!
575
Formulare
Das folgende Beispiel verwendet add(), um eine neue Option einzufügen, und remove(), um die ausgewählte Option zu entfernen. Listing 20.14: add() und remove() (add_remove.html) add() und remove() Option 1 Option 2 Option 3 Option 4 Option 5
Die Wirkung dieses Beispiels gleicht Listing 20.13. Das Ergebnis sehen Sie in Abbildung 20.18.
576
Formularelemente
Es gäbe noch weitere Möglichkeiten, neue Optionen einzufügen. Beispielsweise ließe sich ein neues Element mit der Methode createElement() erstellen.
INFO
Auswahlliste als Navigationselement Einer der häufigsten Einsatzzwecke ist, Auswahllisten mit Links zu versehen und als Navigationselemente oder kleine Sitemaps zu verwenden. Die Methoden, die bei dieser Aufgabe angewendet werden, sind alle vergleichbar. Der einzige Unterschied liegt in der Art der Datenspeicherung. Folgende Möglichkeiten erscheinen sinnvoll:
14 15
Ablegen der URLs in einem Array. Der Index der URL im Array sollte dem Index der Option entsprechen. Ablegen der URLs direkt in der Auswahlliste im value-Attribut.
16
Weniger sinnvoll ist es, die URLs als Text der Auswahlliste zu verwenden, da sie meist entweder nichts sagend oder zu lang sind.
17
In der Praxis ist die zweite Methode am häufigsten anzutreffen. Hier sehen Sie ein einfaches Beispiel:
18
■ ■
Listing 20.15: Eine Auswahlliste mit Links (auswahlliste_links.html)
19
Auswahlliste mit Links Markt+Technik Hauser und Wenz Spiegel Online
Im Download-Archiv finden Sie die erste Methode mit den URLs, die im Array gespeichert sind.14 Die Datei hat den Namen auswahlliste_links_array.html und befindet sich im Ordner code\kap20.
20 21 22 23 24
WWW
14 Ein erweitertes Beispiel mit Arrays finden Sie auch im nächsten Abschnitt »Navigation über mehrere Ebenen«.
577
Formulare
Ihrer Fantasie sind – mal wieder – keine Grenzen gesetzt. Sie sollten noch eine Alternative einbauen, falls kein Element angeklickt wurde. Natürlich können Sie den Link auch in einem neuen Fenster öffnen oder auch relative Links verwenden. Sie sollten allerdings nicht Ihre ganze Navigation auf dieser Methode aufbauen, wenn Sie nicht sicher wissen oder anfänglich überprüfen, ob Ihre Nutzer JavaScript besitzen bzw. aktiviert haben. Es ist natürlich auch möglich, den Code zum URL-Wechsel in das onchangeEreignis zu packen. Dies ist meist jedoch nicht sehr übersichtlich.
TIPP
Da die erste Option bei einer einzeiligen Auswahlliste (size="1") nicht von onchange berücksichtigt wird, müssten Sie hier eine Option ohne Link mit einem Text wie beispielsweise »Bitte wählen« einfügen. Navigation über mehrere Ebenen Auswahllisten lassen sich auch dazu verwenden, über mehrere Ebenen zu navigieren. Damit sind komplexere Navigationen möglich. Allerdings haben sich in der Praxis maximal zwei Ebenen bewährt, mehr sollten es nicht sein. Das folgende Beispiel verwendet eine Auswahlliste, die die Unterscheidung der Kategorie ermöglicht. Der Nutzer kann hier zwischen Verlagen oder Nachrichten wählen. Je nach Wahl wird die zweite Auswahlliste mit Verlags-Websites oder Nachrichtenportalen aus den Arrays verlage oder nachr gefüllt. Die Wahl erfolgt in der Funktion über den ausgewählten Index in der Kategorien-Auswahlliste. Die einzelnen Optionen werden in die zweite Auswahlliste mit einer for-Schleife aus dem jeweiligen Array eingelesen: function wechseln() { var kat = document.formular.kategorie; var ang = document.formular.angebot; if (kat.selectedIndex == 1) arr = verlage; else arr = nachr; for(var i in arr) { ang.options[i] = new Option(arr[i]); } }
Die Funktion für die zweite Auswahlliste funktioniert nach einem ähnlichen Prinzip. Wählt der Nutzer einen Eintrag, wird zuerst überprüft, ob ein Element ausgewählt wurde und ob die zweite Liste mehr als ein Element enthält. Ist dies der Fall, stellt eine Fallunterscheidung fest, welche Kategorie in der ersten Auswahlliste ausgewählt ist. Je nach Kategorie wird das richtige URL-Array für Verlage oder Nachrichtenportale verwendet. Die Umleitung erfolgt schließlich mit window.loction.href und der URL, deren Index mit dem in der Liste ausgewählten Eintrag übereinstimmt.
578
Formularelemente
function verweis() { var ang = document.formular.angebot; var kat = document.formular.kategorie; if (ang.selectedIndex != -1 && ang.length > 1) { if (kat.selectedIndex == 1) arr = v_url; else arr = n_url; window.location.href = arr[ang.selectedIndex]; }
14 15
}
Hier sehen Sie den kompletten Code:
16
Listing 20.16: Zwei Auswahllisten bilden bereits eine komplette Navigation (auswahlliste_navi.html). Navigation mit mehreren Auswahllisten 1) { if (kat.selectedIndex == 1) arr = v_url; else arr = n_url;
579
Formulare
window.location.href = arr[ang.selectedIndex]; } } //--> Bitte wählen Verlage Nachrichten
Abbildung 20.19: Der Nutzer wählt die Kategorie VERLAGE (links). Daraufhin erscheinen rechts die erhältlichen Verlage. Wenn sich der Nutzer einen Verlag aussucht …
Abbildung 20.20: … gelangt er auf dessen Homepage.
580
Formularelemente
Verwenden Sie das HTML-Tag , um Auswahllisten noch übersichtlicher zu formatieren. Das Attribut label im Tag enthält eine Beschreibung der Gruppe. Es steht auch als JavaScript-Eigenschaft ab Mozilla 1 und Internet Explorer 6 zur Verfügung.
TIPP
14
20.2.6 Datei-Upload
Der Datei-Upload dient dazu, Dateien auf den Server zu laden. Er bietet dem Nutzer also die Möglichkeit, im Dateisystem des Computers eine Datei zu suchen und diese an den Server zu übertragen. Da der Server hier im Spiel ist, erfolgt die Kontrolle normalerweise über eine serverseitige Technologie wie PHP oder ASP.NET.
15
Beim Datei-Upload muss im -Tag das Attribut enctype auf "multipart/form-data" gesetzt sein. Damit wird das richtige Übertragungsformat für ganze Dateien angegeben.
17
16
TIPP
18
Die einzige wirklich interessante Einstellung in JavaScript ist das valueAttribut. Es enthält den Namen der Datei, die hochgeladen werden soll. Diese Eigenschaft kann beispielsweise in JavaScript überprüft werden, um festzustellen, ob eine Datei angehängt wurde. Listing 20.17:
19
Überprüfen, ob beim Datei-Upload eine Datei angehängt ist (datei_upload.html)
20
Datei-Upload
21 22 23 24
581
Formulare
Abbildung 20.21: JavaScript testet und meldet, dass keine Datei angehängt wurde.
Mehr zur Vollständigkeitsüberprüfung für andere Formularelemente erfahren Sie in Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke«. REF
20.2.7 Schaltflächen Gelegentlich – beispielsweise bei Auswahllisten als Navigationselementen – benötigt ein Formular keine Schaltfläche. Dies wird allerdings eher die Ausnahme sein. In den meisten Fällen muss der Nutzer nach dem Ausfüllen des Formulars eine Schaltfläche anklicken, die das Formular versendet, in eine Datenbank schreibt oder überprüft. Die nächsten Punkte zeigen die Funktionsweise der verschiedenen Schaltflächen. Button Das -Tag ist eine Schaltfläche, die unabhängig von einem Formular ist, also keine einschließenden -Tags benötigt. Diesen Vorteil erkaufen Sie sich damit, dass diese Art von Schaltflächen erst in neueren Browsern ab Netscape 6 und Internet Explorer 4 funktioniert. Tabelle 20.16: -Tag
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Der Konqueror stellt die Schaltfläche über die gesamte Fensterbreite dar. Dies ist der optischen Wirkung natürlich nicht zuträglich. Abhilfe schafft nur die Angabe einer Breite per Stylesheet-Befehl mit width.
582
Formularelemente
Heißt das, Sie sollten das -Tag meiden? In vielen Fällen kann es durchaus sinnvoll sein, da Sie darin jeden beliebigen HTML-Code (außer Formularelementen und Links) einschließen können. So lässt sich auch ein Bild auf die Schaltfläche legen. JavaScript ist die beste Möglichkeit, die Schaltfläche zum Leben zu erwecken. Denkbar sind verschiedene Ereignisse wie onmouseover oder onclick.
14
Das folgende Beispiel setzt onclick ein, um zu zählen, wie oft der Nutzer die Schaltfläche betätigt:
15
Listing 20.18: Eine Schaltfläche mit -Tag (button.html) Button Klicken!
16 17 18 19 Abbildung 20.22: Der Autor verwendet kostbare Zeit darauf, diese Schaltfläche achtmal anzuklicken …
20 21 22 23 24
583
Formulare
TIPP
Das -Tag kann auch die Funktionsweise eines Submit- oder ResetButtons annehmen. Ändern Sie dazu einfach das Attribut type auf submit oder reset. Die Standardeinstellung für type ist button. Diese muss allerdings nicht angegeben werden. Normale Schaltfläche Die normale Schaltfläche erhält ihre Funktionalität wie ebenfalls erst über JavaScript bei verschiedenen Ereignissen wie onclick und onmouseover. Da die Schaltfläche allerdings Teil des Formulars ist, können Sie darauf auch wie auf ein Formularfeld zugreifen. Also entweder mit Namen: document.Formularname.Schaltflächenname;
oder über die forms- und elements-Kollektionen. Dies benötigen Sie nicht, wenn Sie mit der Schaltfläche eine Funktion starten möchten, die sich um andere Formularelemente kümmert.15 Wollen Sie allerdings eine Eigenschaft der Schaltfläche selbst ändern, benötigen Sie den Zugriff. Im folgenden Beispiel wird die Eigenschaft value verwendet, um die Beschriftung einer Schaltfläche zu ändern, wenn der Nutzer sie anklickt. Die Beschriftung enthält, wie oft der Nutzer den Button angeklickt hat. Listing 20.19: Die Eigenschaft einer Schaltfläche ändern (input_button.html) Normale Schaltfläche
15 In den letzten Abschnitten werden die normalen Schaltflächen sehr häufig so verwendet.
584
Formularelemente
Abbildung 20.23: Die Schaltflächenbeschriftung zeigt, wie oft der Nutzer den Button angeklickt hat.
14 15 Das onclick-Ereignis reagiert in diesem Beispiel in den Browsern zu langsam. Das heißt, der Nutzer kann mit der Maus öfter klicken, als die Funktion ausgeführt wird. In dieser Anwendung hat dies keine große Bedeutung. Wenn Sie allerdings eine Schaltfläche für ein Spiel verwenden wollen, sollten Sie diese Zeitverzögerung einkalkulieren, da sie nicht verhindert werden kann.
16 HALT
17 18
Neben der value-Eigenschaft gibt es noch einige weitere Eigenschaften, die die folgende Tabelle zusammenfasst. Sie gelten nicht nur für die normale Schaltfläche mit type="button", sondern auch für Submit- und Reset-Buttons (siehe die folgenden beiden Abschnitte), nicht aber für Schaltflächen. Eigenschaft
Beschreibung
defaultValue
Standardwert und damit Standardbeschriftung einer Schaltfläche. Erscheint immer, wenn ein Formular mit einem Reset-Button zurückgesetzt wird. Kann auch geändert werden.
name
Der Name einer Schaltfläche im Code. Er kann geändert werden.
size
Die Breite einer Schaltfläche in Zeichen. Auch diese Eigenschaft kann per JavaScript geändert werden.
type
Art des Buttons. Kann die Ausprägungen button, submit oder reset annehmen. Kann nur gelesen werden.
value
Der Wert, der gleichzeitig die Beschriftung der Schaltfläche ist. Er kann ebenfalls geändert werden. Browser vor Internet Explorer 4, Mozilla und Opera 7 passen allerdings die Größe der Schaltfläche nicht automatisch an die neue Beschriftung an.
19 Tabelle 20.17: Wichtige Eigenschaften für Schaltflächen
20 21 22 23 24
585
Formulare
Tabelle 20.18: Wichtige Eigenschaften für Schaltflächen
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
defaultValue
name
size
type
value
Submit Der Submit-Button () hat zwar die gleichen Eigenschaften wie eine normale Schaltfläche, kann allerdings auch ohne JavaScript eine Handlung vornehmen. Er versendet das Formular.16 Die Informationen, die zum Versand benötigt werden, stehen als HTMLAttribute im -Tag: ■ ■
method – steht für die Versandmethode. action – enthält die Adresse, an die versendet wird. Hier wird auch unterschieden, ob der Versand an ein serverseitiges Skript oder per E-Mail erfolgt.
In Abschnitt 20.3 »Formular versenden … « zeigen wir Ihnen die verschiedenen Versandmethoden, Versandarten und den Versand per JavaScript. REF
Neben der Möglichkeit des Versands gibt es allerdings noch ein Ereignis, das eintritt, wenn der Nutzer den Submit-Button anklickt. Dieses Ereignis heißt onsubmit und steht ebenfalls im -Tag. Dort muss es true zurückliefern, damit das Formular versendet wird.17 Tabelle 20.19: onsubmit onsubmit
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
onsubmit wird in der Praxis dazu verwendet, zu überprüfen, ob das Formular vollständig ausgefüllt wurde oder ob der Nutzer es tatsächlich abschicken möchte.18
16 Da Browser tolerant sind, kann – oder besser sollte – ein Formular nur einen Submit-Button besitzen. 17 Diese Rückgabe ähnelt der, die für das Schreiben in die Statusleiste beim onmouseover- bzw. onmouseout-Ereignis notwendig ist (siehe Abschnitt 16.4 »Statusleiste«). 18 Die Vollständigkeitsüberprüfung finden Sie in Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke«.
586
Formularelemente
Das folgende Beispiel fragt beim Nutzer noch einmal mit einem Bestätigungsdialog (window.conform) nach, ob die Daten übertragen werden sollen. Die Übertragung erfolgt dann an ein serverseitiges Skript (im action-Attribut): Listing 20.20: Vor der Weiterleitung nachfragen (input_submit.html) Versand überprüfen
14 15 16 17 18 Abbildung 20.24: Vor der Übertragung wird der Nutzer gefragt.
19 20 21 22 23 24
Mit können Sie auch eine Grafik zur VERSENDEN-Schaltfläche machen. Für den JavaScript-Programmierer ändert sich dadurch nichts.
TIPP
587
Formulare
Reset Die Reset-Schaltfläche setzt alle Formularelemente auf die Standardwerte zurück. Auch hier ist zuerst kein JavaScript erforderlich. Allerdings kann es sinnvoll sein, nachzufragen, ob der Nutzer seine Formularwerte tatsächlich vollständig löschen möchte. Hierfür gibt es das Ereignis onreset im Tag. Es erwartet wie onsubmit eine Rückgabe von true. Tabelle 20.20: onreset onreset
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel zeigt eine Überprüfung, bevor das Formular geleert wird: Listing 20.21:
Das Leeren des Formulars überprüfen (input_reset.html)
Reset überprüfen
HALT
Trotz Überprüfung sind Reset-Schaltflächen nicht ganz ungefährlich, insbesondere bei längeren Formularen. Sie sollten auf jeden Fall die Reset-Schaltfläche optisch und lokal weit genug entfernt von der Submit-Schaltfläche positionieren (also anders als in unserem einfachen Beispiel). Mit der Methode reset() des form-Objekts19 setzen Sie das Formular mittels JavaScript zurück:
TIPP
document.formular.reset();
Allerdings sollten Sie damit vorsichtig umgehen, um nicht das Formular zu löschen, ohne dass der Nutzer den Grund nachvollziehen kann.
19 Dieses Objekt gehört zum -Tag. Die Methode ist in allen neueren Browsern enthalten.
588
Formularelemente
Abbildung 20.25: Der Nutzer muss bestätigen, ob er das Formular leeren will, …
14 15 16 17 Abbildung 20.26: … und das Textfeld wird dann auf den Standardwert zurückgesetzt.
18 19 20 21 22 23 24
20.2.8 fieldset, legend Die beiden Tags und bilden zusammen die Möglichkeit, ein Formular in mehrere Bereiche aufzuteilen, die jeweils in einem Kasten liegen. Die einzige sinnvolle JavaScript-Eigenschaft für diese Elemente ist align, die die Ausrichtung enthält. Sie entspricht dem HTML-Attribut align. Beachten Sie, dass der Rahmen von über die gesamte Fensterbreite verläuft, wenn Sie nicht per Stylesheet-Angabe eine feste Breite vergeben.
589
Formulare
Tabelle 20.21: , und die zugehörige Eigenschaft align
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
align
Ein Beispiel finden Sie im Download-Archiv unter dem Namen fieldset_ legend.html im Ordner code\kap20. WWW
Den Text im -Tag können Sie beispielsweise mit innerHTML und Zugriff über getElementById() ändern. TIPP
20.2.9 label Das -Tag verbindet mit dem HTML-Attribut for die Beschreibung für ein Formularelement mit dem Element selbst. Bei Radiobuttons oder Checkboxen hat dies den Vorteil, dass ein Klick auf die Beschreibung auch automatisch das Kästchen aktiviert. Aus der Sicht des JavaScripters gibt es dafür nur eine Eigenschaft: htmlFor, mit der Sie die Verbindung der Beschriftung zu einem beliebigen Formularelement herstellen können. Als Wert muss nur die ID (nicht der Name!) des Formularelements übergeben werden. Abbildung 20.27: Ein Anklicken der Beschreibung aktiviert den zugehörigen Radiobutton.21
20
20 Das Beispiel finden Sie im Download-Archiv unter dem Namen label.html im Ordner code\kap20.
590
Formular versenden …
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
htmlFor
Tabelle 20.22: und die zugehörige Eigenschaft htmlFor
14
Browser, die das Tag nicht unterstützen, ignorieren es und stellen nur den Beschreibungstext neben dem Formularelement dar.
15
20.3 Formular versenden … Mit einem Formular muss irgendetwas geschehen. Grundsätzlich haben Sie folgende Möglichkeiten: ■
■ ■ ■
16
Das Formular an ein serverseitiges Skript übergeben. Ein Beispiel und nähere Informationen dazu erhalten Sie in Abschnitt 20.3.1 »… an serverseitiges Skript«. Clientseitiges JavaScript erledigt hier häufig noch vor dem Versand die Vollständigkeitsüberprüfung (siehe Kapitel 21 »Vollständigkeitsüberprüfung und reguläre Ausdrücke«). Das Formular direkt per E-Mail versenden. Nähere Informationen erhalten Sie unter Punkt 20.3.2 »… per E-Mail«. Das Formular mit der JavaScript-Methode submit() versenden. Hierzu finden Sie mehr in Abschnitt 20.3.3 »… per Methode«. Die Formularelemente in ein Cookie speichern und später per JavaScript wieder auslesen. So lässt sich ein Warenkorb mit JavaScript realisieren. Alternativ können Sie die Formularelemente in ein verstecktes Textfeld in einem unsichtbaren Frame speichern. Beide Methoden lernen Sie in Kapitel 18 »Cookies« kennen.
17 18 19 20 21
20.3.1 … an serverseitiges Skript
22
Serverseitige Skripte dienen meist dazu, Formularinhalte auszuwerten, in Datenbanken zu schreiben oder per E-Mail zu verschicken. Um dies zu ermöglichen, benötigen Sie natürlich eine beliebige serverseitige Programmiersprache auf Ihrem Webserver. Im Formular selbst müssen Sie außerdem einige Einstellungen vornehmen: ■
23 24
Die erste Wahl gilt der Versandmethode (method-Attribut des Tags). ■ GET verschickt die Daten nach einem Fragezeichen (?) an die URL angehängt. Die Namen der Formularelemente (name) und die Werte (value) werden paarweise durch Et-Zeichen (&) getrennt hintereinandergehängt. GET hat den Nachteil, dass die Zahl der Zeichen je nach Browser bzw. Webserver beschränkt ist. Spätestens bei 2.000 Zeichen ist Schluss. Außerdem wird die per GET modifizierte URL im
591
Formulare
■
INFO
Browser abgelegt, ist also etwas leichter manipulierbar als das vor den Augen des Anwenders etwas besser versteckte POST. Allerdings ist auch POST mit Werkzeugen, die in den HTTP-Verkehr eingreifen, problemlos manipulierbar. POST sendet die Daten im HTTP-Header, also im Kopf des Webprotokolls HTTP (HyperText Transfer Protocol). Die Formatierung entspricht der bei GET mit Name/Werte-Paaren, die durch Et-Zeichen (&) voneinander getrennt sind.
Die Funktionen escape() und unescape() wandeln Strings in URL-Kodierung um bzw. wieder zurück. Diese und einige neuere Funktionen finden Sie in Abschnitt 4.3.4 »URLs« erläutert. ■
Als Nächstes geben Sie unter action das serverseitige Skript an. Dies kann als absolute URL oder relativ erfolgen.
Beim Versand an den Server schaltet sich JavaScript im Ereignis onsubmit ein, um beispielsweise eine Vollständigkeitsüberprüfung durchzuführen oder eine Nachfrage abzusetzen. Listing 20.20 zeigt ein Beispiel für den serverseitigen Versand mit JavaScript-Nachfrage.
20.3.2 ... per E-Mail Formulare können per E-Mail verschickt werden, indem Sie im action-Attribut mit mailto: eine E-Mail-Adresse angeben.
Die obige Zeile versendet das Formular an die E-Mail-Adresse des Autors. Zusätzlich haben Sie die Möglichkeit, einen Betreff anzugeben:
Der Betreff muss in URL-Kodierung angegeben sein. Mit dem Attribut enctype geben Sie zusätzlich die Art der versendeten Daten an. text/plain steht beispielsweise für puren Text. Wird dieses Attribut weggelassen, erfolgt der Versand als Attachment in URL-kodierter Form.
Beim Empfänger kommt die Mail als Name/Werte-Paar mit allen Formularelementen an. Hat das Formular nur ein Textfeld mit Namen eingabe und der Nutzer gibt test ein, erscheint also eingabe=test
in der Mail.
592
Formular versenden …
Bis jetzt wirkt das hier geschilderte Verfahren – das häufig auch mailto-Verfahren genannt wird – wie der perfekte Weg, um Formulare zu mailen. Leider ist das nicht so. Folgendes Problem trübt das Bild: Der zweite Nachteil wirkt schwerer: Den Versand erledigt nicht der Browser selbst. Er benötigt vielmehr ein E-Mail-Programm, das er zuerst aufrufen muss. Die schlechte Nachricht: Verschiedene E-Mail-Programme verhalten sich sehr unterschiedlich. Manche übernehmen in Verbindung mit bestimmten Browsern den Betreff oder gar die Empfängeradresse nicht korrekt. Viele Nutzer wollen außerdem lieber von ihren Freemail-Accounts eine E-Mail an Firmen schreiben, haben diese aber beispielsweise nicht als Versandadresse in den Mailprogrammen eingegeben.
14 15 Abbildung 20.28: Auch wenn der Versand funktioniert, erhält der Nutzer vorher eventuell Warnmeldungen, die er bestätigen muss.
16 17 18
Unter dem Strich bleibt, dass der Versand mit mailto: auf einem nicht vernachlässigbaren Teil der Rechner (sicherlich 20 %) Schwierigkeiten bereitet. Daher werden in der Praxis meist serverseitige Skripte zum Versand von Formularen verwendet. Die mailto-Methode gilt dagegen als eher unprofessionell und eignet sich maximal für kleinere, privat betriebene Websites.
19
20.3.3 … per Methode
21
20
Mit der Methode submit() können Sie ein Formular per JavaScript übertragen. Damit lässt sich zum einen jedes beliebige anklickbare HTML-Element für den Versand verwenden und zum anderen können Sie bei einer Überprüfung selbst steuern, wann das Formular versendet wird, und sind nicht mehr darauf beschränkt, beim Ereignis onsubmit true zurückzuliefern.
submit()
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
22 23 Tabelle 20.23: Die Methode submit() des form-Objekts
Im folgenden Beispiel wird ein Formular versendet, wenn der Nutzer in das Textfeld »Sesam« eingibt. Dies ist natürlich kein guter Passwortschutz, da der gewünschte Text im Quellcode steht. Es illustriert allerdings die Verwendung von submit():
593
24
Formulare
Listing 20.22: Per Methode versenden (versand_methode.html) Versand per Methode
Abbildung 20.29: Nur das richtige Wort erlaubt den Versand.
594
Formular versenden …
Anweisungen direkt nach der submit()-Methode brechen die Übertragung ab. Das heißt, beispielsweise eine Umleitung auf eine Seite, auf der Sie sich für die E-Mail bedanken, ist auf diese einfache Art nicht möglich. Der Einsatz von setTimeout() ist hier sehr unsicher, da nicht klar ist, wie lange die Übermittlung dauern wird.
HALT
14 15 16 17 18 19 20 21 22 23 24
595
Inhalt
21 Vollständigkeitsüberprüfung und reguläre Ausdrücke 14
15
Eines der häufigsten Einsatzgebiete von JavaScript ist die Vollständigkeitsüberprüfung von Formularen. Dabei kann – je nach Aufwand und Anwendung – überprüft werden, ob Felder ausgefüllt und ob korrekte Eingaben erfolgt sind.
16
Für umfangreichere Überprüfungen eignen sich insbesondere die regulären Ausdrücke. Es handelt sich hierbei um Muster, auf die Formularinhalte getestet werden können. Gewollte Falscheingaben wie der berühmte »Donald Duck« sind nicht vollständig zu verhindern. Sie können natürlich im Hintergrund mit einer Adress- oder Kundendatenbank arbeiten, dies ist allerdings recht aufwändig und für ein einzelnes Kontaktformular nicht sinnvoll. Bei einer Vollständigkeitsüberprüfung sollte eher der Servicegedanke im Vordergrund stehen, vom Kunden alle tatsächlich notwendigen Daten zu erhalten. Dass dabei schreibfaule Nutzer nach ein oder zwei Fehlversuchen mit Blindeinträgen auch gezwungen werden, Angaben korrekt vorzunehmen, ist ein angenehmer Nebeneffekt.
21.1
17
18 HALT
19
20
21
Vollständigkeitsüberprüfung
Die Vollständigkeitsüberprüfung kann an verschiedenen Stellen in einem Dokument erfolgen:
22
Beim Abschicken des Formulars (onsubmit im -Tag) Direkt nach der Eingabe eines Werts in das Formularfeld (onchangeEreignis)
23
■ ■
Die einfachste und am häufigsten benötigte Überprüfung ist, ob ein Formularelement ausgefüllt wurde. Diese Möglichkeit finden Sie im nächsten Abschnitt für alle Formularelemente. Komplexere Überprüfungen von häufig vorkommenden Beispielen wie Postleitzahlen oder E-Mail-Adressen finden Sie in Abschnitt 21.3 »Reguläre Ausdrücke«.
24
Sie sollten obligatorische Felder, also verpflichtend auszufüllende Formularfelder, als solche kennzeichnen.
597
ndex
TIPP
Vollständigkeitsüberprüfung und reguläre Ausdrücke
21.1.1
Formularelemente
Alle Textfelder werden der gleichen Überprüfung unterzogen, unabhängig davon, ob es sich um mehrzeilige, einzeilige und versteckte Felder oder Passwortfelder handelt. Checkboxen und Radiobuttons sind etwas schwieriger zu prüfen. Auswahllisten müssen nur unter bestimmten Umständen getestet werden. Textfeld, mehrzeiliges Textfeld und andere Felder Die Überprüfung eines Textfelds ist ausgesprochen einfach. Sie müssen lediglich überprüfen, ob es leer ist oder nicht. Das folgende Beispiel zeigt dies: Listing 21.1:
Vollständigkeitsüberprüfung für ein Textfeld (vollst_feld.html)
Vollständigkeit überprüfen
HALT
Diese Überprüfung funktioniert nicht, wenn bereits ein Vorgabewert mit dem value-Attribut angegeben ist. In diesem Fall muss geprüft werden, ob das Feld den Vorgabewert oder einen leeren String enthält, denn beides wäre nicht korrekt.1 Wenn Sie es wünschen, können Sie zusätzlich vorher alle Leerzeichen entfernen, damit der Nutzer Sie nicht mit der Eingabe von Leerzeichen täuschen kann. Listing 21.2:
Vollständigkeitsüberprüfung ohne Leerzeichen (vollst_feld_ohneleer.html)
function test() { var ele = document.formular.eingabe.value; return (ele.replace(" ", "") != ""); }
1
598
Es sei denn, der Vorgabewert ist ein möglicher Wert. Dies sollten Sie jedoch im Allgemeinen vermeiden.
Vollständigkeitsüberprüfung
Die hier gezeigte Überprüfung gilt für alle Arten von Textfeldern. Checkboxen
INFO
Für eine Checkbox können Sie mit checked überprüfen, ob sie aktiviert wurde. Folgende Zeile liefert eine Überprüfung, ob eine Checkbox für die AGB aktiviert wurde: Listing 21.3:
14
Eine Checkbox überprüfen (vollst_checkboxen.html)
return (document.formular.AGB.checked);
Normalerweise ist die Überprüfung einer Checkbox nicht sinnvoll, da sie optionalen Charakter hat. Eine Ausnahme ist eine Checkbox, die eine Bestätigung erfordert: beispielsweise dass der Nutzer die Lizenzbedingungen akzeptiert oder die AGB gelesen hat.
15 16
HALT
17
Wollen Sie überprüfen, ob aus einer Gruppe von Checkboxen2 eine ausgewählt ist, müssen Sie alle Elemente mit einer Schleife durchlaufen: Listing 21.4:
Vollständigkeitsüberprüfung mit mehreren Checkboxen
18
(vollst_checkboxen_mehrere.html) function test() { var ele = document.formular.katalog; for (var i = 0; i < ele.length; i++) { if (ele[i].checked) return (true); } return false; }
19 20 21
Radiobuttons Radiobuttons treten immer in Gruppen auf, haben also dasselbe name-Attribut. Die Überprüfung erfolgt analog zu den Checkboxen: Listing 21.5:
22
Vollständigkeitsüberprüfung für eine Gruppe von Radiobuttons (vollst_radiobutton.html)
23
function test() { var ele = document.formular.geschl; for (var i = 0; i < ele.length; i++) { if (ele[i].checked) return (true); } }
24
Auswahllisten Bei Auswahllisten unterscheidet sich die Vollständigkeitsüberprüfung für Listen mit nur einer und Listen mit mehreren Auswahlmöglichkeiten.
2
Jede Checkbox einer Gruppe hat das gleiche name-Attribut.
599
Vollständigkeitsüberprüfung und reguläre Ausdrücke
Auswahlliste mit einer Option Wenn der Nutzer keine Option auswählt, ist automatisch die erste Option in einer Auswahlliste ausgewählt. Die Eigenschaft selectedIndex hat also den Wert 0.3 Daher steht häufig als erste Option nur ein Platzhalter wie »Bitte wählen« zur Verfügung. Bei einer Vollständigkeitsüberprüfung kann anschließend getestet werden, ob selectedIndex gleich 0 ist. In diesem Fall hat der Nutzer nichts ausgewählt. return (ele.selectedIndex != 0);
Wollen Sie nicht nur am Anfang, sondern auch zwischen den Optionen leere Optionen einfügen, sollten Sie eine andere Überprüfungsmethode wählen. Die Füllfelder sollten einen leeren String als value haben. Das Überprüfungsskript prüft dann die value-Eigenschaft der ausgewählten Option: Listing 21.6:
Die Vollständigkeitsüberprüfung mittels eines leeren value-Attributs
(vollst_auswahl_value.html) Vollständigkeit überprüfen Option 1 Option 2 --- Option 3
Auswahlliste mit mehreren Optionen Bei einer Auswahlliste mit mehreren Optionen4 kann der Nutzer natürlich auch nur leere Optionen auswählen. Die Auswahl bleibt auch dann korrekt, wenn nur eine Option einen Wert hat. Daher muss die Überprüfungsfunktion alle Optionen durchgehen und überprüfen, ob sie ausgewählt sind und keinen leeren Wert haben. Nur wenn beides für eine der Optionen zutrifft, wird von der Überprüfung true zurückgeliefert. 3 4
600
Siehe Abschnitt 20.2.5 »Auswahlliste«, Abschnitt »Zugriff auf Auswahllisten«. HTML-Attribut multiple.
Vollständigkeitsüberprüfung
Listing 21.7:
Vollständigkeitsüberprüfung für eine Auswahlliste mit mehreren Optionen
(vollst_auswahl_mehrere.html) function test() { var ele = document.formular.auswahl; for (var i = 0; i < ele.length; i++) { if (ele.options[i].value != "" && ele.options[i].selected) return true; } return false; }
14 15
Datei-Upload Die Überprüfung des Datei-Uploads erfolgt wie die Überprüfung eines Textfelds. Ist es leer, hat der Nutzer keine Datei angegeben.
16
In Abschnitt 20.2.6 »Datei-Upload« finden Sie ein Beispiel für die Überprüfung des Datei-Uploads.
17 REF
21.1.2 Automatisieren
18
Die bisherigen Überprüfungen für die einzelnen Formularelemente benötigen eine direkte Referenz auf den Elementnamen. Dies ist bei größeren Formularen natürlich unpraktisch. Hier stellt sich die Frage, ob das ganze Formular – unabhängig davon, welche Elemente enthalten sind – nicht automatisch geprüft werden kann. Ein solcher Automatismus basiert immer auf demselben Aufbau, auch wenn er unterschiedlich realisiert werden kann: ■ ■ ■ ■
19 20
Alle Elemente des Formulars werden in einer Schleife durchgeprüft. Eine Fallunterscheidung testet den Typ (type-Eigenschaft) des Elements. Je nach Typ wird eine der Überprüfungsfunktionen aufgerufen. Die gesamte Überprüfung wird mit return false abgebrochen, wenn eine der Überprüfungsfunktionen false zurückliefert, ein Element also nicht ausgefüllt ist.
21 22 23
Im Folgenden sehen Sie ein vollständiges Skript, das aus diesen Elementen besteht. Hervorgehoben sind die Funktionsaufrufe und die Rückgaben (return). Listing 21.8:
24
Automatisierte Vollständigkeitsüberprüfung (vollst.html)
Vollständigkeit überprüfen
Wohin soll die Reise gehen?
Zypern --- Barcelona --- Moskau Reiseziel? Sonderwunsch? Weiblich Männlich Katalog senden
602
Vollständigkeitsüberprüfung
Die Überprüfungsfunktionen kennen Sie bereits von den einzelnen Formularelementen. Lediglich die Funktion für Checkboxen und Radiobuttons musste angepasst werden: ■
14
Hier wird als Erstes überprüft, ob mehrere Elemente mit demselben Namen vorhanden sind. function radio_check(f, ele) { if (f.elements[ele].length == null) { return true;
■
15
Wenn dies nicht der Fall ist, wird sofort true zurückgeliefert. Das heißt, Checkboxen oder Radiobuttons, die allein stehen, müssen nicht unbedingt aktiviert sein. Gibt es mehrere Checkboxen oder Radiobuttons mit einem Namen, werden alle Elemente der Gruppe durchlaufen und überprüft, ob eines der Elemente aktiviert ist.
16 17
} else { for (var i = 0; i < f.elements[ele].length; i++) { if (f.elements[ele][i].checked) return true; } } ■
18 19
Sollte dies nicht der Fall sein, wird am Ende der Funktion false zurückgegeben.
20
return false; }
21 Optionale Felder Der Automatismus vereinfacht das Leben des JavaScript-Programmierers natürlich deutlich, macht allerdings auch ein wenig unflexibel. Was geschieht beispielsweise, wenn ein Formular optionale Felder haben soll? Natürlich könnte jedes Feld einzeln ausgeschlossen werden, allerdings ist dies recht umständlich. Daher können Sie auf einen Trick zurückgreifen: Geben Sie jedem Feld, das optional sein soll, einen eindeutigen Namenszusatz wie beispielsweise "_opt". Überprüfen Sie anschließend mit indexOf()innerhalb der Schleife, ob der Namenszusatz enthalten ist.5 Nur wenn dies nicht der Fall ist, wird das Element weitergeprüft: Listing 21.9:
22 23 24
Optionale Elemente nicht testen (Ausschnitt aus: vollst_opt.html)
function test(f) { for (var i=0; i < f.elements.length; i++) { if (f.elements[i].name.indexOf("_opt") == -1) { if (f.elements[i].type=="text" || f.elements[i].type=="password" || f.elements[i].type=="textarea") { 5
Wenn dies nicht der Fall ist, ergibt indexOf() den Wert –1 (siehe Abschnitt 7.2.5 »Suchen«).
603
Vollständigkeitsüberprüfung und reguläre Ausdrücke
if(!leer(f.elements[i])) return false; } else if (f.elements[i].type=="select-multiple") { if(!multiple(f.elements[i])) return false; } else if (f.elements[i].type=="checkbox" || f.elements[i].type=="radio") { if(!radio_check(f, f.elements[i].name)) return false; } } } return true; } . . . Sonderwunsch?
21.1.3 Nachfragen Bisher erfolgte die Fehlermeldung ohne sinnvolle Ausgabe. Für eine Ausgabe benötigen Sie eine Fehlervariable, die die Namen der nicht vollständig ausgefüllten Formularfelder aufnimmt. Listing 21.10: Vollständigkeitsüberprüfung mit Fehlermeldung (vollst_fehler.html) var fehler = ""; function test(f) { for (var i=0; i < f.elements.length; i++) { if (f.elements[i].type=="text" || f.elements[i].type=="password" || f.elements[i].type=="textarea") { if(!leer(f.elements[i])) fehler += f.elements[i].name + " "; } else if (f.elements[i].type=="select-multiple") { if(!multiple(f.elements[i])) fehler += f.elements[i].name + " "; } else if (f.elements[i].type=="checkbox" || f.elements[i].type=="radio") { if(!radio_check(f, f.elements[i].name)) fehler += f.elements[i].name + " "; } } if (fehler != "") { alert("Folgende Felder fehlen:\n" + fehler); fehler = ""; return false; } else { return true; } }
604
Vollständigkeitsüberprüfung
Abbildung 21.1: Das Skript liefert eine Fehlermeldung.
14 15 16 17 18 19 Allerdings ist noch nicht alles perfekt. Ist die Radiobutton-Gruppe nicht ausgefüllt, erscheint die Fehlermeldung zweimal, weil sie aus zwei Radiobuttons besteht. Hier hilft eine einfache Überprüfung, ob das Feld bereits im fehler-String genannt wurde:
20 21
Listing 21.11: Für Gruppen von Radiobuttons und Checkboxen erscheint nun lediglich eine Fehlermeldung (vollst_fehler_mehrere.html). } else if (f.elements[i].type=="checkbox" || f.elements[i].type=="radio") { if(!radio_check(f, f.elements[i].name)) { if (fehler.indexOf(f.elements[i].name) == -1) fehler += f.elements[i].name + " ";
22
}
23
}
Wenn Sie etwas längere oder aussagekräftigere Fehlermeldungen verwenden möchten, sollten Sie nicht das name-Attribut heranziehen. Dann scheitert der Automatismus und Sie müssen für jedes Feld eine eigene Fehlermeldung anlegen. Als Erweiterung können Sie die Fehler auch auf einer neuen Seite6 oder mit innerHTML7 oben in der Seite ausgeben.
6 7
24 TIPP
Siehe unter Punkt 21.2 »Formulardaten übernehmen«. Siehe Kapitel 23 »Dynamisches«, insbesondere Abschnitt 23.1.4 »Neuere Ansätze«.
605
Vollständigkeitsüberprüfung und reguläre Ausdrücke
21.1.4 Ändern In der Regel kann der Nutzer nach der Vollständigkeitsüberprüfung das Formular neu ausfüllen. Wollen Sie etwas aufdringlicher nachfragen, können Sie auch window.prompt() verwenden und direkt nach einem Wert für das Feld nachfragen. Dies ist hauptsächlich bei Textfeldern sinnvoll, nicht aber bei Auswahllisten oder Checkboxen.
HALT
Im Allgemeinen erscheint die Nachfrage nach einem Formularwert zu aufdringlich. Außerdem bietet sich hier eine Endlosschleife an, wenn der Nutzer das Feld nicht ausfüllen möchte.
21.1.5 Überprüfung bei der Eingabe Die Vollständigkeitsüberprüfung bei der Eingabe erfolgt über das Ereignis onchange. Neben dem Formularfeld muss dann dynamisch eine Fehlermeldung angezeigt werden. Eine Möglichkeit besteht darin, einen bereits vorhandenen -Block einzublenden. Listing 21.12: Vollständigkeitsüberprüfung bei der Eingabe (vollst_eingabe.html) Vollständigkeit überprüfen Bitte ausfüllen
Das hier verwendete Skript kann für jedes Textfeld in der Seite genommen werden. Die Referenz auf das aktuelle Textfeld wird mit this übergeben und der Name in der Funktion dynamisch zusammengesetzt: var name = e.name + "_aus";
606
Vollständigkeitsüberprüfung
Dies erfordert, dass die -Blöcke nach einem bestimmten Schema benannt sind. In dem Beispiel heißen sie immer wie das Textfeld, gefolgt von "_aus". Abbildung 21.2: Der Nutzer hat nur ein Leerzeichen eingetragen.
14 15 16 17
Das onchange-Ereignis hat den Nachteil, dass es nicht aufgerufen wird, wenn der Nutzer nur in das Feld klickt und nichts ändert. Für diesen Fall könnten Sie zusätzlich onclick verwenden, allerdings erscheint dann sofort eine Fehlermeldung, wenn der Nutzer in das Feld klickt.
18 TIPP
19
Um das vorherige Beispiel auch mit der vierten Generation von Netscape Navigator und Internet Explorer abwärtskompatibel zu machen, ist einiger Aufwand erforderlich. Der Netscape Navigator 4.x benötigt einen absolut positionierten -Block für die Beschriftung und in die Funktion test() muss eine Fallunterscheidung für den Zugriff auf den -Block eingefügt werden.
20 21
Sie finden das abwärtskompatible Beispiel im Download-Archiv unter vollst_ eingabe_komp.html im Ordner code\kap21.
22
WWW
Um auch in Uraltbrowsern die höchste Abwärtskompatibilität zu erreichen, können Sie statt eines -Blocks auch ein image-Objekt verwenden und das Bild von einem transparenten GIF in ein Symbol für fehlerhaftes Ausfüllen abändern.8
23 24
21.1.6 Auf dem Server? Gegenüber einer serverseitigen Vollständigkeitsüberprüfung hat die Überprüfung mit JavaScript Vor- und Nachteile. Zunächst die schlechte Nachricht: JavaScript kann deaktiviert werden. Das heißt, entweder entfällt die Vorausfüllung oder der Nutzer kann das Formular überhaupt nicht mehr verschicken. Letzteres sollte natürlich auf keinen Fall eintreten, wenn Sie keine Kunden verlieren möchten. Da aber auch der erste Fall nicht angenehm ist, 8
Mehr Informationen zum image-Objekt erhalten Sie in Kapitel 15 »Bilder«.
607
Vollständigkeitsüberprüfung und reguläre Ausdrücke
bleibt zu überlegen, direkt zu überprüfen, ob der Nutzer JavaScript aktiviert hat, und ihm nur dann das Formular mit Vollständigkeitsüberprüfung vorzusetzen. Allerdings halten Sie auch hier dem Nutzer ohne JavaScript Informationen vor. Bei diesem Dilemma sollten Sie überlegen, ob eher der Servicegedanke oder die Filterung von Falscheingaben im Vordergrund steht.
TIPP
Für elektronische Zahlungssysteme – beispielsweise per Kreditkarte – stellen viele Dienstleister zusätzliche Überprüfungen zur Verfügung, sodass sie den Zahlungsverkehr im Onlineshop gegenchecken können, ohne selbst programmieren zu müssen. Ein Vorteil von JavaScript gegenüber serverseitiger Überprüfung ist die Performance. Die JavaScript-Überprüfung wird direkt im Browser aufgebaut. Es ist keine Anfrage an den Server erforderlich. Bei der serverseitigen Überprüfung müssen die Daten dagegen zunächst an den Server geschickt werden, anschließend wird überprüft und eventuell auftretende Fehlermeldungen werden zurückgeliefert. Dies ist auch der Grund, warum ASP.NET serverseitige Überprüfungen mit Validation Controls für den Internet Explorer9 in JScript-Code umwandelt.
INFO
21.2
Formulardaten übernehmen
Es gibt mehrere Möglichkeiten, um Daten aus einem Formular auf eine andere Seite zu übernehmen. Im Einzelnen erfolgt dies über: ■ ■
■
ein Cookie (siehe Kapitel 18 »Cookies«) versteckte Formularfelder (Kapitel 20 »Formulare«), eventuell in einem versteckten, also nur 1 Pixel hohen Frame (vergleiche Kapitel 19 »Frames«). In einem versteckten Frame können Sie natürlich auch die Werte in Variablen ablegen. die Übergabe in der URL mit GET. Diese Methode verwenden wir in diesem Abschnitt, um eine Dankesseite und die Eingaben zu bestätigen.
Im Dokument, das die Dankesseite aufruft, muss die URL der Seite unter action eingetragen werden. Als Methode ist GET zu wählen. Listing 21.13: Aufruf der Dankesseite (vollst_danke.html)
WWW
Dieses Skript ist eine leichte Abwandlung von Listing 21.11. Sie finden die vollständige Datei unter vollst_danke.html im Ordner code\kap21 im Download-Archiv.
9
608
Andere Browser müssen mit serverseitiger Überprüfung vorliebnehmen.
Formulardaten übernehmen
Die Formulardaten werden hinter einem Fragezeichen an die URL angehängt. Die einzelnen Name/Wert-Paare sind durch Et-Zeichen (&) voneinander getrennt: danke.html?Reiseziel=Zypern&Sonderwunsch=Sitzplatz&Geschlecht=m&Katalog=on
Darauf können Sie in der Dankesseite mit dem location-Objekt zugreifen. location.search enthält den Teil des Strings mit dem Fragezeichen. Die Dankesseite ist folgendermaßen aufgebaut: ■
14
Nach einigen Variablendeklarationen erhält die Variable ur_str den String vom Fragezeichen (inklusive) bis zum Ende. Ist der String nur ein Zeichen oder leer, werden keine Werte übergeben. Wenn jedoch Werte vorhanden sind, erhält der String statt des Fragezeichens am Anfang ein Et-Zeichen (&), da so die Überprüfung der Name/Wert-Paare einfacher erfolgt. Anschließend wird die Funktion ausgeben() aufgerufen, die den String aus der URL zerlegen soll.
15 16 17
var ur_str = location.search; if (ur_str.length > 2) { ur_str = "&" + ur_str.substring(1, ur_str.length); ausgeben(ur_str); } else { document.write("
Keine Werte übertragen
"); } ■
18 19
Diese Funktion geht in einer Schleife den gesamten String durch und schneidet die Namen und Werte heraus. Dazu werden anhand der Trennzeichen & und = jeweils Beginn (beg), Mitte (mit) und Ende (end) eines Name/Wert-Paares herausgefiltert.
20
function ausgeben(ur_str) { for(var pos = 0; ur_str.indexOf("&", pos) != -1; pos=mit+1) { beg = ur_str.indexOf("&", pos)+1; mit = ur_str.indexOf("=", pos); if(ur_str.indexOf("&", mit+1) != -1) end = ur_str.indexOf("&", mit+1); else end = ur_str.length; feld = unescape(ur_str.substring(beg, mit)); wert = unescape(ur_str.substring(mit+1,end));
21 22 23 24
document.write("
"+feld+": "+wert+"
"); } }
Nachfolgend sehen Sie den vollständigen Code: Listing 21.14: Eine Dankesseite (danke.html) Vielen Dank
609
Vollständigkeitsüberprüfung und reguläre Ausdrücke
Vielen Dank für das Interesse!
Sie haben Folgendes ausgewählt:
2) { ur_str = "&" + ur_str.substring(1, ur_str.length); ausgeben(ur_str); } else { document.write("
Keine Werte übertragen
"); } function ausgeben(ur_str) { for(var pos = 0; ur_str.indexOf("&", pos) != -1; pos=mit+1) { beg = ur_str.indexOf("&", pos)+1; mit = ur_str.indexOf("=", pos); if(ur_str.indexOf("&", mit+1) != -1) end = ur_str.indexOf("&", mit+1); else end = ur_str.length; feld = unescape(ur_str.substring(beg, mit)); wert = unescape(ur_str.substring(mit+1,end)); document.write("
"+feld+": "+wert+"
"); } } //-->
Abbildung 21.3: Die Angaben werden noch einmal zusammengefasst.
610
Reguläre Ausdrücke
Wenn Sie kein automatisiertes Skript wählen, sondern auf bekannte Formularelemente zurückgreifen, können Sie auch eigene Angaben machen und beispielsweise den Wert m aus dem value-Attribut mit »männlich« ersetzen. Ein weiterer Verbesserungsvorschlag wäre, das Skript zusätzlich mit einer Versandmöglichkeit zu verbinden.
TIPP
14 21.3
Reguläre Ausdrücke
Reguläre Ausdrücke sind Muster, die in Strings gesucht werden können oder mit denen Strings verglichen werden. Natürlich können Sie auch mit den String-Methoden wie indexOf() in Strings suchen (siehe Kapitel 7 »Arrays und Strings«). Reguläre Ausdrücke haben allerdings eine eigene Syntax, die es ermöglicht, auch komplexe Überprüfungen sehr einfach durchzuführen.
15
In JavaScript stehen reguläre Ausdrücke als eigenes Regular ExpressionObjekt zur Verfügung. Ein solches Objekt können Sie mit dem Konstruktor (new) erzeugen
17
var reg = new RegExp("\d{5}");
18
16
oder als Literal definieren: var reg = /\d{5}/;
19
Standardisiert sind reguläre Ausdrücke in ECMAScript v3.10 NS4.x Regular Expression
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Tabelle 21.1: Reguläre Ausdrücke und Regular ExpressionObjekt
Beachten Sie, dass das Literal mit dem regulären Ausdruck immer in Schrägstriche (/) eingeschlossen ist. Der Unterschied zwischen beiden Methoden ist folgender: ■
■
20 21 22
Per Konstruktor wird der reguläre Ausdruck erst kompiliert, wenn er aufgerufen wird. Dies erlaubt Ihnen, den regulären Ausdruck im Verlauf des Skripts zu ändern. Sie können den Ausdruck allerdings auch mit der Methode compile("Reg. Ausdr.", "Parameter") dazu zwingen, kompiliert zu werden. Diese Methode steht in allen Browsern zur Verfügung, die reguläre Ausdrücke enthalten. Per Literal wird der reguläre Ausdruck sofort kompiliert.
23 24
Das Erstellen eines regulären Ausdrucks ist teilweise recht kompliziert, weil die Syntax sehr kurz und daher auch etwas unübersichtlich ist. Nachfolgend zeigen wir Ihnen eine kurze Übersicht über die wichtigsten grammatikalischen Regeln für reguläre Ausdrücke: 10 JavaScript 1.2 übernimmt einen Teil des Standards, während JavaScript 1.5 den kompletten Standard übernimmt. Die regulären Ausdrücke in ECMAScript v3 entsprechen funktional größtenteils Perl 5.
611
Vollständigkeitsüberprüfung und reguläre Ausdrücke
■ ■
Ziffern und Buchstaben des Alphabets entsprechen in regulären Ausdrücken sich selbst. Sonderzeichen werden mit Backslash (\) entwertet. var reg = /\\/
■
■
■
■
■
entspricht also einem Backslash. Zeichen mit besonderer Bedeutung – so genannte Metazeichen – sind: \|[]()^$*+-.?. Ihre Bedeutung wird nachfolgend erläutert: Zeichenarten können in eckigen Klammern ([]) angegeben werden. Der reguläre Ausdruck /[136]/ prüft, ob eine Ziffer 1, 3 oder 6 ist. Mit dem Bindestrich können Sie Bereiche festlegen: /[a-z]/ steht für alle Kleinbuchstaben und /[a-z0-9]/ für alle Kleinbuchstaben und Ziffern. Für einige Zeichenarten gibt es Kürzel: \w steht für alle ASCII-Zeichen und \d für alle Ziffern. Der Punkt (.) steht für jedes beliebige Zeichen und \s für ein Whitespace. Wollen Sie eine Zeichenart bewusst ausschließen, schreiben Sie ein ^ an den Anfang in die eckigen Klammern: /[^136]/ steht also für »alles« außer 1, 3 und 6. Zum Ausschließen einiger Zeichenarten gibt es ebenfalls Kürzel: \W steht für alles außer ASCII-Zeichen und \D für alles außer Ziffern. \S steht für kein Whitespace. Die Häufigkeit des Auftretens eines Zeichens lässt sich in geschweiften Klammern festlegen: /\d{5}/ steht also für das fünfmalige Auftreten einer Ziffer. Sie können mit Komma getrennt auch Minimal- und Maximalanzahl des Auftretens festlegen: /\d{4,5}/ besagt, dass zwischen vier und fünf Ziffern vorkommen müssen. Wenn Sie die Stelle nach dem Komma freilassen, steht dies für »mindestens«: /\d{5,}/ verlangt also mindestens fünf Ziffern. Neben dieser Syntax gibt es noch einige Kurzformen: ? steht für einmaliges oder kein Auftreten. Dies entspricht {0,1}. + steht für einmaliges oder mehrmaliges Auftreten. Dies hat die gleiche Wirkung wie {1,}. ■ * steht für mehrmaliges oder kein Auftreten. Dies ist dasselbe wie {0,}. Klammern dienen zur Zusammenfassung von Elementen. Der horizontale Strich (|) trennt Alternativen. /(01|12)|[4-9]/ liefert true, wenn das Zeichen entweder die Ziffer 4 bis 9 enthält oder 0 und 1 oder 1 und 2. ■ ■
■
TIPP
612
Den Inhalt einer Klammer können Sie mit \Nummer wieder aufrufen, wobei die Nummer der Position der Klammer entspricht. Die erste Klammer heißt also \1, die zweite \2 usw. Auf diese Weise können Sie Konstrukte in regulären Ausdrücken wiederverwenden.
Reguläre Ausdrücke
■
■
Ankerzeichen: ^11 steht für den Anfang des Strings oder der Zeile, $ für das Ende. \b (Reguläre Ausdrücke) entspricht einer Wortgrenze. \B ist true, wenn es sich um keine Wortgrenze handelt. Flags regeln das Verhalten des regulären Ausdrucks und werden immer an das Ende geschrieben: /[a-d]/i, oder per Konstruktor: new RegExp("[a-d]", "i"). Die für JavaScript wichtigen Flags sind: ■
■
■
14
g – ist ein Vergleich, der nicht nach der ersten Übereinstimmung abgebrochen wird. i – ist ein Vergleich, der die Groß- und Kleinschreibung nicht berücksichtigt. m – berücksichtigt Strings mit Zeilenumbrüchen (mit \n).
15 16
Dieses Buch kann keine umfassende Einführung in reguläre Ausdrücke bieten. Das Standardwerk auf diesem Gebiet, in dem Sie weiterführende Informationen finden, heißt »Reguläre Ausdrücke« von Jeffrey E. F. Friedl und ist in der deutschen Fassung bei O’Reilly erschienen.
INFO
17
21.3.1 Mit regulären Ausdrücken arbeiten
18
Um mit regulären Ausdrücken effektiv arbeiten zu können, müssen die verschiedenen Erscheinungsformen zunächst von den regulären Ausdrücken unterschieden werden. Dies erfolgt über:
19
■
Drei String-Methoden – match(RegExp), replace(RegExp, Ersatzstring) und search(RegExp), die auf ein String-Objekt einen regulären Ausdruck anwenden. Die folgenden Zeilen ergeben a als Ausgabe, da der Buchstabe »a« in dem String »Mozilla« enthalten ist:
20 21
var text = "Mozilla"; var reg = /[a-d]/; alert(text.match(reg));
22 Auch die String-Methode split(Reg. Ausdr.) kann einen regulären Ausdruck verwenden, um einen String zu unterteilen. Die folgenden Zeilen teilen »Mozilla« in »Moz« und »lla«. Das Trennzeichen im regulären Ausdruck verschwindet:
INFO
23
var reg = /i/; text.split(reg); ■
■
24
Die Methoden eines Regular Expression-Objekts – sie erlauben, mit dem per Literal oder Konstruktor geschaffenen Regular ExpressionObjekt zu arbeiten. Mehr dazu erfahren Sie im nächsten Abschnitt. Das globale RegExp-Objekt mit seinen Eigenschaften – dieses Objekt ist immer vorhanden. Es ist nicht gleichzusetzen mit dem Konstruktor RegExp. Dieser Konstruktor erzeugt ein bestimmtes Regular Expression-
11 Beachten Sie die unterschiedliche Bedeutung der Zeichen in verschiedenen Kontexten.
613
Vollständigkeitsüberprüfung und reguläre Ausdrücke
Objekt. Das RegExp-Objekt ist dagegen global und enthält in seinen Eigenschaften Informationen über die zuletzt durchgeführten Operationen an einem (dem zuletzt erstellten oder geänderten) Regular Expression-Objekt.12 Die wichtigsten Eigenschaften dieses Objekts finden Sie nachfolgend zusammengefasst. Beachten Sie, dass alle Eigenschaften bis auf input nur lesbar sind: ■
■
■
■
■
■
■
■
Tabelle 21.2: Methoden des RegExp-Objekts
$1, $2 … $9 – Diese neun Eigenschaften geben die Teile des regulären Ausdrucks an, die in runde Klammern gepackt sind. Sie können zwar im regulären Ausdruck beliebig viele Unterteilungen vornehmen, dieses Objekt speichert allerdings nur neun. index – gibt die Position im String an, an der der reguläre Ausdruck zum ersten Mal zutraf. input – enthält den String, auf den eine Suche mit dem regulären Ausdruck durchgeführt wurde, und ist als Einzige der Eigenschaften auch setzbar. Die Kurzform in Anlehnung an Perl lautet $_. Sie können sie statt des Eigenschaftsnamens einsetzen. lastIndex – ist eine Position nach der Stelle, an der der reguläre Ausdruck zuletzt zutraf. lastMatch – sind die zuletzt mit einem regulären Ausdruck gefundenen Zeichen. Die Kurzform lautet $&. leftContext – enthält den Teil des Strings links neben einem Teil, der dem regulären Ausdruck entsprochen hat. rightContext – ist das Gegenstück zu leftContext. Die Eigenschaft enthält den rechten Teil neben dem zutreffenden Stück. multiline – gibt an, ob der reguläre Ausdruck auf einen einzeiligen oder mehrzeiligen String (Flag m) angewendet wurde.
$1, $2…
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
index input
lastIndex
lastMatch
leftContext
rightContext
multiline
12 Diese Unterscheidung wird in vielen Quellen nur unsauber oder überhaupt nicht getroffen. Sie ist jedoch entscheidend, wenn Sie Eigenschaften richtig anwenden möchten.
614
Reguläre Ausdrücke
Methoden des Regular Expression-Objekts Für ein Regular Expression-Objekt gibt es zwei wichtige Methoden: ■
exec("String") – liefert ein Array mit der ersten übereinstimmenden Stelle zwischen regulärem Ausdruck und String. Die weiteren Indizes des Arrays liefern eventuell vorhandene Unterergebnisse von Array-Teilen in runden Klammern. Gibt es keine Übereinstimmung, lautet das Ergebnis null. Die Methode ähnelt in der Wirkung sehr stark der match()-Methode des String-Objekts. Diese speichert jedoch alle Übereinstimmungen in einem Array.
14 15
var text = "JavaScript"; var reg = /[aeiou]/; var erg = reg.exec(text);
16
liefert »a«. Der Index 1 (erg[1]) ist nicht vorhanden, da der reguläre Ausdruck keine einzelnen Teile hat. Für das Ergebnis-Array gibt es drei Eigenschaften: input, index und lastIndex. Sie gleichen denen des RegExp-Objekts, denn die jeweils zuletzt ausgeführte Regular Expression-Suche vererbt ihre Werte an die Eigenschaften dieses Objekts. input enthält im vorherigen Beispiel also den String »JavaScript«, index die Position, an der der reguläre Ausdruck zutraf. Dies ist hier die 1 für das erste »a«. lastIndex ist die Position nach dem ersten Zutreffen, also 2. ■
17 INFO
18 19
test("String") – prüft, ob der reguläre Ausdruck im String eine Übereinstimmung findet. Wenn dies der Fall ist, wird true zurückgeliefert, sonst false. Die Zahl der Übereinstimmungen ist irrelevant. Das Ergebnis der folgenden Zeilen ist also true, da in »JavaScript« Vokale vorkommen:
20 21
var text = "JavaScript"; var reg = /[aeiou]/; var erg = reg.test(text);
22
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
exec()
test()
Tabelle 21.3: exec() und test()
23 24
21.3.2 Anwendungen Nachdem Sie nun wissen, wie Sie in JavaScript reguläre Ausdrücke bilden, beschäftigen wir uns im Folgenden mit einigen Mustern, die die Vollständigkeitsüberprüfung wesentlich erleichtern.
615
Vollständigkeitsüberprüfung und reguläre Ausdrücke
Zahl und Postleitzahl Das folgende Beispiel überprüft, ob in ein Formularfeld nur Zahlen eingegeben wurden. Wenn dies nicht der Fall ist, wird das Formular nicht abgeschickt: Listing 21.15: Überprüfung, ob nur Zahlen in ein Feld eingetragen wurden (reg.html) Reguläre Ausdrücke
Aus diesem einfachen Beispiel lässt sich sehr einfach eine Überprüfung für Postleitzahlen erarbeiten. Deutsche Postleitzahlen bestehen immer aus 5 Ziffern. Folgender regulärer Ausdruck prüft dies: var reg = /\d{5}/;
Darüber hinaus werden hier auch Buchstaben vor oder hinter den fünf Zahlen akzeptiert. Um diese auszuschließen, erlauben Sie zwischen Anfang und Ende des Strings nur fünf Zahlen: Listing 21.16: Deutsche Postleitzahlen prüfen (postleitzahl.html) var reg = /^\d{5}$/;
TIPP
Natürlich gibt es alternative Wege: Sie können das Feld auf fünf Stellen begrenzen und Anfangs- und Endankerzeichen im regulären Ausdruck weglassen oder Sie verwenden bei einem fünfstelligen Feld den Test mit isNaN(). Dies würde Sie allerdings nicht vor der Eingabe von Fließkommazahlen bewahren. Telefonnummern Telefonnummern sind schwierig zu prüfen. Sie müssen dazu für Ihr Formular ein vorgegebenes Eingabeformat erzwingen. Dies ist allerdings auch nicht ohne Risiko, da beispielsweise Nutzer aus dem Ausland eventuell noch die Auslandskennung mit vorangehendem Plus angeben möchten.
616
Reguläre Ausdrücke
Wenn Sie die Eingabe von Telefonnummern ohne sonstige Zeichen verlangen, können Sie sehr einfach überprüfen, ob genügend Ziffern angegeben sind: var reg = /^\d{7,}$/;
Wenn Sie das Format der Telefonnummer vollständig freigeben, können Sie prüfen, ob nur die üblichen Zeichen für Telefonnummern vorhanden sind. Hervorgehoben sind die Steuer- und Entwertungszeichen des regulären Ausdrucks:
14
Listing 21.17: Telefonnummer prüfen (telefonnummer.html)
15
var reg = /^(\d|\+|\-|\.|\,|\/|\(|\))*$/;
16
Datum Bei den Daten kommt es meist darauf an, ob es sich um ein gültiges Datum im richtigen Format handelt. Hier ist es besonders wichtig, ein Format vorzugeben. In Deutschland werden Daten beispielsweise in der Form TT.MM.JJ oder TT.MM.JJJJ angegeben, in Amerika dagegen in der Form MM.TT.JJ oder MM.TT.JJJJ.
17 18
Ein deutsches zwei- oder vierstelliges Datum mit Punkten als Trennzeichen können Sie beispielsweise auf diese Weise prüfen (die Steuerzeichen für reguläre Ausdrücke sind hervorgehoben):
19
Listing 21.18: Datum prüfen (datum.html) var reg = /^(0?[1-9]|[12][0-9]|3[01]).(0?[1-9]|1[0-2]).((19|20)?\d{2})$/;
20
Bei dieser Prüfung wird jedoch nicht berücksichtigt, dass die Monate unterschiedlich lang sein können.
21 E-Mail Die Überprüfung von E-Mail-Adressen mit regulären Ausdrücken gehört zu den schwierigsten Disziplinen. Der folgende reguläre Ausdruck erledigt diese Prüfung bereits relativ zuverlässig:
22
Listing 21.19: Prüfen einer E-Mail-Adresse (email.html)
23
var reg = /^([a-zA-Z0-9_\.\-])+@(\[)?([a-zA-Z0-9_\.\-])*([a-zA-Z0-9]{2,})\. ([a-zA-Z]{2,4}|[0-9]{1,3}\])$/;
24
Der reguläre Ausdruck besteht aus folgenden Teilen (runde Klammern hervorgehoben): ■
Vor dem Klammeraffen dürfen Ziffern, Zahlen, Unterstriche, Punkte und Minuszeichen stehen. Es muss jedoch mindestens ein Zeichen sein (dies gewährleistet das +): ([a-zA-Z0-9_\.\-])+
■
Dann folgt der Klammeraffe. Beachten Sie, dass dies der einzige Klammeraffe für die E-Mail-Adresse ist. @
617
Vollständigkeitsüberprüfung und reguläre Ausdrücke
■
Der nächste Abschnitt gibt an, dass an dieser Stelle optional (also keinmal oder einmal mit ?) eine eckige Klammer folgt. Dies fängt alle E-MailAdressen ein, die in dem gültigen Format name@[123.123.123.123] geschrieben sind. (\[)?
■
Nun dürfen beliebig viele Zahlen, Ziffern, Unterstriche, Punkte und Minuszeichen folgen. ([a-zA-Z0-9_\.\-])*
■
Vor dem nächsten Punkt müssen jedoch mindestens zwei Zahlen oder Ziffern für die Second-Level-Domain stehen: ([a-zA-Z0-9]{2,})
■
Dann folgt der letzte Punkt: \.
■
Und anschließend zwei bis vier Ziffern für die Top-Level-Domain: ([a-zA-Z]{2,4}
■
Oder drei Ziffern für die letzte Zahl der IP-Adresse und die schließende eckige Klammer: |[0-9]{1,3}\])
HALT
Auch dieser bereits recht komplexe Ausdruck ist nicht zu umgehen. Er schließt jedoch zumindest keine gültigen E-Mail-Adressen aus und bietet ein meist ausreichendes Maß an Sicherheit.
21.4
Nutzer
Viele Probleme mit obligatorischen Feldern und der Vollständigkeitsüberprüfung können Sie vermeiden, wenn Sie beim Anlegen des Formulars auf gute Verwendbarkeit achten. Die wichtigsten Regeln sind: ■ ■
■
■ ■
Nur tatsächlich notwendige Felder sollten obligatorisch sein. Trennen Sie Angaben, bei denen es Probleme mit dem Format geben könnte. Geburtsdaten lassen sich beispielsweise einfach auf drei Textfelder aufteilen, sodass es keine Verwechslungen mit englischem und deutschem Format, Punkten und Leerzeichen geben kann. Wenn es nur einige wenige Optionen gibt, fassen Sie diese in einer Auswahlliste zusammen. Dies ist bei der Jahreszahl des Geburtstags sicherlich nicht sinnvoll, aber bei wenigen Optionen wie der Versandmethode nützlich. Beschriften Sie die Felder ausreichend und geben Sie deutlich an, welche obligatorisch sind. Trennen Sie die Submit- und die Reset-Schaltfläche optisch und lokal weit genug voneinander, damit versehentliches Anklicken der Schaltflächen vermieden wird.13
13 Sie können auch bei der Reset-Schaltfläche erst nachfragen (siehe Abschnitt 20.2.7 »Schaltflächen«, Abschnitt »Reset«, und Listing 20.20).
618
Nutzer
■
■
Strukturieren Sie das Formular in logische Teile. Die Adresse sollte beispielsweise deutlich von bestellten Elementen oder angeforderten Katalogen abgehoben sein. Lassen Sie Platz bzw. Felder für eigene Anmerkungen des Nutzers. Manche Kunden möchten Ihnen weitere Mitteilungen zukommen lassen.
14 15 16 17 18 19 20 21 22 23 24
619
Inhalt
22 CSS und JavaScript 14
15
Die Zeiten, in denen Webdesigner dachten, sie müssten keine Stylesheets verwenden, da es das -Tag auch tut, sind – glücklicherweise – vorbei. Cascading Style Sheets (CSS) dienen dazu, HTML-Dokumente flexibel und vor allem dokumentübergreifend einheitlich zu formatieren. Außerdem erlaubt dies die komplette Trennung von Layout, sprich CSS, Inhalt in Form von HTML und Logik bzw. Funktion in Form von JavaScript. CSS wird in mehr oder weniger strikter Form von allen gängigen Browsern unterstützt.
16
17
Der CSS-Standard obliegt der Obhut des W3C (World Wide Web Consortium). CSS ist eigentlich eine eigene Sprachsyntax, die Formatierungen für HTML-Elemente1 festlegt. Das W3C hat bereits mehrere Versionen des CSSStandards herausgegeben.
■
■
Da dieses Buch kein CSS-Buch ist, behandelt es keine CSS-Grundlagen und Befehle. Sie finden dazu allerdings sowohl online als auch offline viele Quellen. Erste Anlaufstelle ist die Spezifikation. Zusätzlich ist eine browserunabhängige Quelle empfehlenswert, die auf die Browserunterschiede eingeht. Mehrere Beispiele von CSS und JavaScript im Zusammenspiel finden Sie auch in den folgenden Kapiteln.
1 2 3
19
CSS1 – Dieser Standard ist bereits seit Dezember 1996 eine Recommendation, hat also den höchsten Rang eines verabschiedeten Standards erreicht. In CSS1 sind die meisten Befehle enthalten, die die Formatierung betreffen.2 CSS2 – ist seit Mai 1998 eine Empfehlung. Er erweitert CSS1 beispielsweise um die Verwendung für verschiedene Medientypen, um verbesserte Positionierung und um Tabellenformatierung.3 CSS3 – befindet sich zurzeit in der Entwicklung. Den aktuellen Entwicklungsstand finden Sie unter http://www.w3.org/Style/CSS/currentwork. Es wird jedoch noch einige Zeit dauern, bis der Standard Einzug in die Browserwelt hält. Unter den geplanten Änderungen sind außerdem wenige Formatierungsanpassungen.
20
21
22 23
24
INFO
CSS ist nicht auf HTML beschränkt, sondern kann beispielsweise auch für XML verwendet werden. Die offizielle Quelle lautet: http://www.w3.org/TR/REC-CSS1. http://www.w3.org/TR/REC-CSS2.
621
ndex
■
18
CSS und JavaScript
22.1
CSS
Als JavaScript-Programmierer müssen Sie nicht alle Stil-Befehle4 kennen. Es ist ausreichend, einige Grundlagen zu verstehen. Stylesheets lassen sich auf der Seite an verschiedenen Stellen unterbringen: ■
Inline-Stylesheets befinden sich im style-Attribut eines HTML-Tags:
Text
■
Der -Block befindet sich dagegen im Kopf der HTML-Seite. Hier können Sie Stil-Befehle für eine bestimmte Tag-Art definieren, beispielsweise alle Absätze (
-Tags). Alternativ oder zusätzlich lassen sich auch Klassen5 deklarieren, die anschließend einem HTML-Element mit dem Attribut class zugewiesen werden.
Listing 22.1:
Stylesheet im Kopf der Seite (css.html)
CSS .klasse {font-weight:bold} CSS
Text
■
Als dritte Alternative können Sie Stylesheets in externe Dateien auslagern. Die externe Datei wird mit oder @import eingefügt. In der Praxis wird meist verwendet, da @import nicht im Netscape 4.x funktioniert.
Ein Testskript zu @import finden Sie im Download-Archiv unter import.html im Ordner code\kap22. WWW
22.1.1 Syntax Die CSS-Syntax selbst ist sehr einfach: Stil-Befehl und Wert sind durch einen Doppelpunkt getrennt. Mehrere Stil-Befehl/Wert-Paare werden durch Semikolon (Strichpunkte) abgeteilt:
Text
4 5
622
Deutsch für Stylesheet-Befehl. Häufig werden die Begriffe Stil-Anweisung oder -Regeln synonym verwendet. Neben den selbst definierten Klassen gibt es so genannte Pseudoklassen, die Formatierungen für unterschiedliche Zustände vornehmen. a:hover ist beispielsweise die Pseudoklasse für das Aussehen eines Links, wenn der Nutzer mit der Maus darüberfährt.
CSS
Der Stylesheet-Block im Kopf der Seite oder das externe Stylesheet enthalten zuerst den oder – durch Kommata getrennt – die Tags, für die die nachfolgenden Stil-Befehle gelten sollen. Fehlt ein Tag, gilt die Definition für alle Elemente mit der Klasse. Durch einen Punkt abgetrennt folgt die Klasse. Fehlt sie, gelten die Stil-Befehle für alle Tags. Ist beides vorhanden, gelten sie nur für die Tags mit der entsprechenden Klasse. Die Stil-Befehle selbst folgen in geschweiften Klammern:
14
Tag.Klasse {Stil-Befehl:Wert}
15
22.1.2 Einheiten Bei den meisten HTML-Attributen, die Abstände oder Größen angeben, haben Sie nur die Wahl zwischen Pixel (Bildpunkten) und eventuell Prozent (%). Bei Stylesheets ist dies anders: Hier existieren viele verschiedene Einheiten, die Sie auch angeben können, wenn Sie die JavaScript-Eigenschaften der Stil-Befehle verwenden (siehe Abschnitt 22.2 »Zugriff«).
16 17
Die wichtigsten Einheiten sind: ■
■ ■
■ ■
18
px – Pixel: Ein Pixel ist ein Bildpunkt. Wie viele Bildpunkte in einer Fläche vorhanden sind, gibt die Auflösung an. Die Auflösung von Monitoren wird in ppi, also Pixel pro Inch, gemessen. Übliche Werte liegen zwischen 72 und 96 ppi. % – Prozent. mm – Millimeter, cm – Zentimeter und in – Inch werden für die Bildschirmausgabe selten verwendet, da sie eigentlich für den Druckbereich gedacht sind. pt – Punkt: Ist die Einheit für den Druck (72 Punkt entsprechen 1 Inch). em – Ein em steht für die Breite des Großbuchstabens M. 1 em entspricht 100 % der Standardschriftgröße (meist 16 px).
19 20 21 22
22.1.3 Farben Die Angabe von Farben kann bei CSS nicht nur mit dem Farbnamen oder hexadezimaler Notierung erfolgen, sondern auch mit dem RGB-Farbwert (Rot, Grün, Blau). Die Farben des Monitors entstammen dem Farbraum und können alle über den Anteil der drei Grundfarben Rot, Grün und Blau angegeben werden. Die Notation sieht wie folgt aus:
23 24
rgb(255,0,0)
Diese Farbe ist ein reines Rot (Anteil 255) ohne Grün- und Blauanteil. In hexadezimaler Schreibweise entspräche das: #ff0000
623
CSS und JavaScript
Die Browser speichern die Farbwerte auch entsprechend der Eingabe sehr unterschiedlich. Folgendes Beispiel verdeutlicht dies: Listing 22.2:
Hintergrundfarbe ändern (farben.html)T6
document.body.style.backgroundColor="#FF0000"; alert(document.body.style.backgroundColor);
Die erste Zeile setzt die Hintergrundfarbe auf den hexadezimalen Wert der Farbe Rot. Die zweite gibt die Hintergrundfarbe aus. Der Internet Explorer gibt #ff0000 zurück, der Mozilla rgb(255,0,0). Dies ist wichtig, wenn Sie beispielsweise mit den Farben rechnen oder die Rückgabe anders verwerten möchten. Abbildung 22.1: Internet Explorer …
Abbildung 22.2: … und Firefox handhaben die Farben intern unterschiedlich.
6
624
Dieses Beispiel ist nicht Netscape Navigator 4.x-kompatibel.
Zugriff
22.2 Zugriff Bevor sich Stile mit JavaScript ändern lassen, benötigen Sie als Erstes Zugriff darauf. Um die Verwirrung nicht zu groß werden zu lassen, ist es notwendig, verschiedene Zugriffsmöglichkeiten zu unterscheiden: ■
Zugriff über das style-Objekt – In diesem Objekt sind die Stil-Befehle als Objekteigenschaften abgelegt und können geändert werden. Die Referenz auf das Element erhalten Sie je nach Browser über den W3C-konformen Weg (document.getElementById) oder für den Internet Explorer 4 über document.all.7
14 15
Element.style.Befehl
Der Netscape Navigator 4.x erlaubt keinen Scripting-Zugriff auf Stil-Befehle mit style. In seinem DOM existiert kein style-Objekt. Allerdings lassen sich einige Angaben, beispielsweise visibility, für ein -Tag direkt ohne style-Objekt vornehmen. Eine Beschreibung der Stylesheet-Implementierung von Netscape 4 in JavaScript finden Sie unter Punkt 22.2.2 »Netscape Navigator 4.x«. NS4.x
style
■
■
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
16 17 Tabelle 22.1: style-Objekt
19
Zugriff auf das -Tag selbst – Der Zugriff erfolgt ebenfalls über den W3C-konformen Weg (document.getElementById) oder für den Internet Explorer 4 über document.all. Beachten Sie, dass das -Tag eine ID haben muss. Der Zugriff ermöglicht mittels der Eigenschaft media den Wechsel des Ausgabemediums8 und über type die Art des Stylesheets.9 Zugriff über das styleSheet-Objekt – Die styleSheets-Kollektion enthält alle Stylesheets einer Seite und erlaubt darüber den Zugriff. Damit können Sie das zugewiesene Stylesheet wechseln (vergleiche Abschnitt 22.3.2 »Stylesheets wechseln«). Unterhalb der styleSheet-Objekte ist jede StilRegel ein eigenes Objekt, das in den verschiedenen Browsern jedoch unterschiedlich angesprochen wird. Mehr dazu erfahren Sie in Abschnitt 22.4 »styleSheets-Kollektion und Regeln«.
20 21 22 23 24
22.2.1 Das style-Objekt und seine Eigenschaften Der eigentliche Zugriff auf Stil-Befehle erfolgt über das style-Objekt. Die beiden weiteren Alternativen sind in der Praxis weniger verbreitet und dienen eher dazu, mehrere Stil-Anweisungen gleichzeitig auszutauschen. 7 8 9
18
Für den Hintergrund existiert ein eigenes Objekt body. Der Zugriff erfolgt dann über document.body. style. In CSS2 können Sie Stylesheets für unterschiedliche Zielmedien wie Print (Druck) und Screen (Bildschirm) festlegen. Da die meisten Browser ausschließlich CSS verwenden, ist diese Änderung in der Praxis nicht notwendig.
625
CSS und JavaScript
Aus der Sicht der Spezifikation gibt es neben dem herkömmlichen DOM, das den Zugriff auf verschiedene Elemente der Seite erlaubt, auch ein eigenes Objektmodell für CSS. Die Spezifikation dazu finden Sie unter http:// w3.org/TR/DOM-Level-2-Style. Das style-Objekt repräsentiert eigentlich nur das style-Attribut bei einem Inline-Stylesheet. Daraus ergeben sich die folgenden einfachen Schritte, um auf den Stil eines Elements zuzugreifen: 1.
Zunächst benötigen Sie eine Referenz auf das Objekt. Dies kann – wie im Fall von body – ein anderes Objekt sein oder Sie greifen mit document.getElementById("ID") bzw. für den Internet Explorer 4 document.all auf ein HTML-Element zu. 2. Anschließend folgt das style-Objekt. 3. Den Abschluss bildet der Stil-Befehl. Der Stil-Befehl ist eine Eigenschaft des style-Objekts. Da in JavaScript die übliche Schreibweise von Stil-Befehlen in CSS mit Minus als Trennzeichen jedoch nicht gut geeignet ist, werden die Stil-Befehle als JavaScript-Eigenschaften ohne Bindestrich geschrieben. Dafür beginnt das zweite Wort mit einem Großbuchstaben. Aus dem CSS-Befehl font-size
wird also die JavaScript-Eigenschaft fontSize
TIPP
Wenn Sie in der Praxis eine style-Eigenschaft benötigen, sehen Sie in einer CSS-Referenz wie http://www.css4you.de/ nach und wandeln den Befehl nach diesem Schema um. Sie sollten jedoch immer testen, da einige Befehle (häufig bereits in CSS) nicht auf allen Browsern die gewünschten Ergebnisse erzielen. Berüchtigt sind hier auch die Unterschiede zwischen dem Internet Explorer unter Windows und dem Mac. Das folgende Beispiel ändert das Aussehen der Schrift, wenn der Nutzer mit der Maus darüberfährt. Die Referenz auf das Element realisiert dieses Skript mit this im Funktionsaufruf. this übergibt die Referenz auf das Objekt, das aufruft. Als Stile werden Schriftgröße und Fettschreibung geändert: Listing 22.3:
Ein mit CSS realisierter Rollover-Effekt (rollover_css.html)
Rollover mit CSS CSS CSS-Homepage des W3C
14 15 Abbildung 22.3: Der Link ist normal formatiert, aber …
16 17 18 19 20
Abbildung 22.4: … wenn der Nutzer mit der Maus darüberfährt, wird er hervorgehoben.
21 22 23 24
Für die Stil-Änderung beim Rollover können Sie natürlich auch die CSS-Pseudoklasse a:hover verwenden. TIPP
627
CSS und JavaScript
22.2.2 Netscape Navigator 4.x Der Netscape Navigator 4.x spielt – was das Scripting von CSS betrifft – eine Sonderrolle. Da er das style-Attribut nicht unterstützt, ist der Skriptzugriff nicht wie in den anderen Browsern gewährleistet. Der Skriptzugriff fehlt jedoch nicht vollständig, sondern Netscape verfolgte zu dieser Zeit noch einen eigenen Weg und nannte ihn JavaScript-Stylesheets (JSSS). Für diesen Weg gibt es drei Kollektionen: classes[], id[] und tags[]. Sie können also Stile für Stil-Klassen, IDs oder Tags vergeben. Dies klingt an sich sehr sinnvoll. Das Problem besteht jedoch darin, dass ein Element sich nicht mehr ändern lässt, wenn es bereits gerendert ist. Daher ist DHTML hier nur eingeschränkt möglich. Ein Anwendungsbeispiel – das Ändern von Stilen beim Laden der Seite – finden Sie im Download-Archiv unter nn4.html im Ordner code\kap22. WWW
22.3 Klassen und Stylesheets wechseln Über das style-Objekt können Sie zwar nahezu jeden Stil ändern, müssen dies allerdings immer einzeln erledigen. Wenn es einmal mehrere Änderungen gleichzeitig sein sollen, wechseln Sie am besten direkt die Klasse oder das komplette Stylesheet.
22.3.1 Klassen wechseln Das Wechseln von Klassen erfolgt über das className-Objekt. Dieses Objekt repräsentiert das class-Attribut für ein beliebiges HTML-Element. Normalerweise werden Objekte in JavaScript so wie die HTML-Attribute10 benannt, da class allerdings ein reserviertes (aber noch nicht verwendetes) Schlüsselwort ist, musste die Alternative className seine Stelle einnehmen. Tabelle 22.2: classNameObjekt
NS4.x
className
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Mit folgender Zeile weisen Sie einem Element eine neue Klasse zu: Element.className = "Klasse";
Ein Beispiel illustriert dies: Das Skript ändert die Klasse, wenn der Nutzer mit der Maus über den Link fährt (onmouseover). Listing 22.4:
Die Stylesheet-Klasse ändern (klasse_wechseln.html)
Rollover mit Klassenwechsel 10 Beispielsweise style.
628
Klassen und Stylesheets wechseln
.ueber {font-weight:bold;color:red;font-size:20pt} .raus {color:blue;font-size:14pt} CSS CSS-Homepage des W3C
Der Konqueror kommt hier in manchen Versionen mit den Klassenwechseln in den Ereignissen onmouseover und onmouseout nicht zurecht, obwohl er className unterstützt. Auch bei anderen Ereignissen ändert der Konqueror die Klasse erst, wenn der Nutzer das Objekt mit der Maus verlässt.
14 15 16 17 18 HALT
19
IDs wechseln
20
Neben den Klassen können Sie zum Zuweisen von Stilen auch IDs verwenden. Die Beschränkung für IDs ist, dass eine ID nur einmal verwendet werden darf. Sollten Sie eine einzelne ID mit Stylesheets mehrmals vergeben, erscheint im Browser allerdings keine Fehlermeldung. Die Festlegung, dass eine ID (Identifier) nur für ein Element gelten darf, bestimmt nur der Standard. Da es zusätzlich sehr unlogisch und unsauber wäre, einen Identifier mehrmals zu vergeben, sollten Sie auf jeden Fall darauf verzichten.
21 22
Das Wechseln der ID erfolgt analog zum Wechseln von Klassen (siehe voriger Abschnitt). Hier heißt das Objekt jedoch nicht idName, sondern lediglich id.
23
Ein Beispielskript finden Sie im Download-Archiv unter id_wechseln.html im Ordner code\kap22.
24
WWW
22.3.2 Stylesheets wechseln Externe und in -Blöcken im Kopf der Seite befindliche Stylesheets sind auch so genannte styleSheet-Objekte.11 Sie werden in der Kollektion styleSheets bereitgehalten und erhalten – in der Reihenfolge des Erscheinens im Code – einen Index, beginnend bei 0. 11 Inline-Stylesheet-Anweisungen sind keine styleSheet-Objekte, können also auch nicht alle gleichzeitig geändert werden. In diesem Fall müssen Sie mit Klassen arbeiten.
629
CSS und JavaScript
Der Zugriff auf ein styleSheet-Objekt erfolgt über den Index: document.styleSheets[Index]
Der Zugriff über die ID ist nur im Internet Explorer möglich. Der Grund ist, dass das styleSheet-Objekt eigentlich nicht dasselbe ist wie das Element-Objekt des -Tags. Der Internet Explorer gibt an, dass das styleSheet-Objekt die ID des -Element-Objekts erbt, der Netscape Navigator und andere Browser gestatten dies nicht.12 document.styleSheets["ID"]
Tabelle 22.3: styleSheetObjekt
NS4.x
styleSheet
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
disabled Um ein Stylesheet zu wechseln, gibt es mehrere Möglichkeiten: Die einfachste ist, das aktive Stylesheet zu deaktivieren und ein neues zu aktivieren. Dies erfolgt über die Eigenschaft disabled. Tabelle 22.4: disabled-Eigenschaft
NS4.x
disabled
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Diese Eigenschaft ist im Konqueror zwar vorhanden, wird allerdings nicht unterstützt. Ändern Sie den Wert per Skript, wird er zwar richtig ausgegeben, beide Stylesheets bleiben jedoch aktiv. Das HTML-Attribut disabled wird ebenfalls nicht unterstützt. Für Konqueror und Opera sollten Sie also das Standardstylesheet in der Reihenfolge als Letztes anfügen, damit seine Stile die der anderen Stylesheets überschreiben. Dies vermeidet unschöne Effekte. Im folgenden Beispiel erhält der Nutzer über eine Schaltfläche die Möglichkeit, das aktive Stylesheet zu wechseln. Das eine stellt die Schrift in Arial, den Hintergrund rot und die Tabellenüberschriften fett dar. Das andere sorgt für einen blauen Hintergrund, weiße Farbe und kursiven Schriftschnitt: Listing 22.5:
Das Stylesheet wechseln (css_wechseln.html)
CSS wechseln body {font-family:Arial;background-color:red} td {font-family:Arial;color:yellow} .tabueb {font-weight:bold} body {background-color:blue} 12 Dies gilt auch für mit eingebundene Stylesheets.
630
Klassen und Stylesheets wechseln
td {color:white} .tabueb {font-style:italic} CSS wechseln
Eigenschaft | Beschreibung |
disabled | Deaktiviert ein Stylesheet. |
Stylesheet wechseln
14 15 16 17 18 19 Abbildung 22.5: Ein Anklicken der Schaltfläche wechselt …
20 21 22 23 24
631
CSS und JavaScript
Abbildung 22.6: … das aktive Stylesheet.
INFO
Die Eigenschaft disabled gibt es auch für -Element-Objekte, wenn Sie darauf beispielsweise mit getElementById() zugreifen. Wesentlich gebräuchlicher ist allerdings der Zugriff über die styleSheets-Kollektion, da sie auch mit eingebundene Stylesheets enthält. Weitere Methoden Neben disabled gibt es einige alternative Methoden, die allerdings alle einen Haken haben: ■
■
■
Für ein styleSheet-Objekt gibt es die Eigenschaft href, die die URL des Stylesheets enthält. Da diese Eigenschaft allerdings in den meisten Browsern mit Ausnahme des Internet Explorers ab Version 4 unter Windows (nicht beim Mac) nicht gesetzt werden kann, sollte sie in der Praxis nicht zum Einsatz kommen. Die Methode addImport(URL) des styleSheet-Objekts erlaubt die Angabe eines externen Stylesheets, das importiert werden soll. Die zugehörige – und nur lesbare – Eigenschaft imports enthält alle importierten styleSheet-Objekte als Array.13 Diese Methode ist allerdings auf den Internet Explorer ab Version 4 beschränkt. Die Methode createStyleSheet(URL) erzeugt ein neues styleSheetObjekt, das auch direkt angewendet wird. Sie funktioniert nur im Internet Explorer ab Version 4. Das neue styleSheet-Objekt ist sofort Teil der
13 Formal-logisch ist jedes importierte Objekt wiederum ein styleSheet-Objekt und unterstützt auch die zugehörigen Eigenschaften wie disabled oder href.
632
styleSheets-Kollektion und Regeln
styleSheets-Kollektion, also des DOM, und kann – auch wenn es leer erstellt wurde – mit anderen Methoden wie beispielsweise addRule()14 gefüllt werden.
Da die hier vorgestellten Methoden auf einige wenige Browser oder Plattformen beschränkt sind, haben sie in der Praxis wenig Einsatzberechtigung.
14 22.4 styleSheets-Kollektion und Regeln
15
Die styleSheets-Kollektion15 hat noch mehr zu bieten außer dem Wechsel von Stylesheets (siehe voriger Abschnitt). Ein styleSheet-Objekt aus der Kollektion enthält mehrere Stil-Befehle oder auch -Regeln. Diese Regeln wiederum sind ebenfalls Objekte und in einer eigenen Kollektion versammelt.
16
22.4.1 Regeln
17
Haben Sie es bemerkt? Im letzten Abschnitt war der Name der Kollektion mit Regeln nicht zu finden. Dies ist keine Unterlassungssünde, sondern den unterschiedlichen Implementationen in den Browsern zu verdanken. In den verschiedenen Browsern haben die Kollektionen daher auch unterschiedliche Namen: ■ ■
19
cssRules – ist die Kollektion in Netscape, Mozilla und Konqueror. rules – heißt sie im Internet Explorer. Einzige Ausnahme ist der Internet Explorer 5 beim Macintosh, denn er unterstützt beide Namen für die Kollektion. NS4.x
cssRules
M/FF
IE4
rules a.
18
IE5 ()
IE5.5
IE6
IE7
SF/KQ
Tabelle 22.5: Regelkollektionen
21
a
Op
20
22
Beim Mac ab Version 5.
23
Der Zugriff erfolgt bei beiden direkt über den Index. Jede Regel (Block mit optionalem Tag, Klasse und den Stil-Befehlen in geschweiften Klammern) erhält eine Indexnummer, beginnend bei 0. Die erste Regel des ersten styleSheet-Objekts wird also wie folgt angesprochen:
24
document.styleSheets[0].cssRules[0];
Oder im Internet Explorer: document.styleSheets[0].rules[0];
14 Siehe Abschnitt 22.4.1 »Regeln«. 15 Der Name styleSheet-Objekt ist übrigens nicht vollständig regelkonform. Eigentlich müsste das Objekt CSSStyleSheet-Objekt heißen.
633
CSS und JavaScript
Setzen können Sie einen Stil-Befehl mit dem style-Objekt document.styleSheets[0].cssRules[0].style.color = "green";
bzw. document.styleSheets[0].rules[0].style.color = "green";
Die unterschiedlichen Namen der Kollektion zwingen den Programmierer in der Praxis dazu, eine Fallunterscheidung zu schreiben. Dazu sind, wie in Kapitel 14 »Browserunterscheidung« nachzulesen, verschiedene Ansätze möglich. Im folgenden Beispiel kommt document.all zum Einsatz, um die Internet Explorer-Varianten herauszufiltern. Das Skript ändert die Schriftart in den zwei Regeln für den Körper der HTML-Seite (body) und für Tabellenzellen (td). Die neue Schriftart kann der Nutzer in ein Textfeld eingeben. Auf eine Überprüfung der korrekten Eingabe verzichtet das Skript, damit der Code nicht zu umfangreich wird. Hier der komplette Code: Listing 22.6:
Mit der Regelkollektion browserübergreifend arbeiten (regeln.html)
Regeln body {font-family:Arial;background-color:red} td {font-family:Arial;color:white} .tabueb {font-weight:bold} CSS wechseln
Eigenschaft | Beschreibung |
disabled | Deaktiviert ein Stylesheet. |
634
styleSheets-Kollektion und Regeln
Abbildung 22.7: Der Nutzer tippt die gewünschte Schriftart ein, klickt auf die Schaltfläche, …
14 15 16 17 18 19 20
Abbildung 22.8: … und die Schrift wird über die Regelkollektionen geändert.
21 22 23 24
635
CSS und JavaScript
22.4.2 Weitere Eigenschaften und Methoden Das styleSheet-Objekt hat noch weitere Eigenschaften und Methoden, die gelegentlich zum Einsatz kommen. Die folgende Tabelle gibt einen Überblick über ausgewählte Eigenschaften des styleSheet-Objekts. Tabelle 22.6: Einige weitere Eigenschaften des styleSheetObjekts
Tabelle 22.7: Weitere Eigenschaften des styleSheetObjekts
Eigenschaft
Beschreibung
cssText
String mit den Stylesheet-Regeln. Nur ab Internet Explorer 5.
media
Enthält und erlaubt das Setzen eines bestimmten Medientyps für ein Stylesheet. Dies entspricht dem HTML-Attribut media.
ownerNode owningElement owningNode
Alle drei Eigenschaften enthalten eine Referenz auf den Dokument-Knoten des styleSheet-Objekts. Dies ist meist das - oder -Element. ownerNode ist die Netscape-Variante ab Version 6, owningElement die Internet Explorer-Variante ab Version 4 und owningNode die (falsche) Benennung im Internet Explorer 5 für den Mac.
parentStyleSheet
Referenz auf das styleSheet-Objekt, das ein importiertes Stylesheet aufnimmt. Bei per @import eingebundenen Stylesheets ist das Ergebnis null, da sie direkt importiert werden.
readOnly
Zeigt an, ob ein Stylesheet extern (true) ist oder innerhalb eines -Blocks definiert wurde (false). Funktioniert nur im Internet Explorer ab Version 4.
title
Wert des title-Attributs eines Stylesheets; setz- und lesbar.
NS4.x
M/FF
IE4
cssText media ownerNode
readOnly title
IE5.5
IE6
IE7
owningElement parentStyleSheet
IE5
Op
SF/KQ
Die Methoden des styleSheet-Objekts dienen dazu, Regeln hinzuzufügen und zu entfernen. Wie die folgende Tabelle zeigt, sind die Methoden für Internet Explorer und Netscape Navigator unterschiedlich.
636
Stylesheets und Ajax
Methode
Beschreibung
addRule()
Fügt eine neue Regel hinzu. Ist der optionale Index angegeben, wird die Regel an dieser Stelle eingefügt, anderenfalls am Ende; Internet Explorer ab Version 4.
Tabelle 22.8: Weitere Methoden des styleSheetObjekts
insertRule("Rule", Index) Fügt eine neue Regel ein; nur Netscape 6 und höher sowie Mozilla.
14
removeRule(Index)
Entfernt eine Regel; Internet Explorer ab Version 4.
deleteRule(Index)
Löscht eine Regel; Netscape Navigator ab Version 6 und Mozilla.
15 NS4.x
M/FF
addRule()
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
insertRule()
deleteRule()
removeRule()
Tabelle 22.9: Weitere Methoden des styleSheetObjekts
16 17
18 22.5 Stylesheets und Ajax
19
Der Einsatz von Stylesheets ist heute für einen Webdesigner selbstverständlich. Die Zugriffs- und Arbeitsmöglichkeiten mit JavaScript liefern dafür aber sehr interessante Möglichkeiten. Verzichtet man wie heute meistens üblich auf Netscape 4.x-Kompatibilität, lassen sich die wichtigsten Elemente wie beispielsweise das style-Objekt bedenkenlos verwenden. Schwieriger in Sachen Browserkompatibilität ist beispielsweise disabled für Konqueror- und OperaNutzer. Hier sind die anderen Möglichkeiten unter Umständen vorzuziehen. Testen Sie insbesondere auf den Browsern, die eine Funktion, ein Objekt usw. nicht unterstützen, um zu sehen, ob auch Nutzer dieser Browser mit Ihrer Seite noch etwas anfangen können. Wenn dies nicht der Fall ist, müssen Sie diesen Nutzern Alternativen anbieten.
20 21 22 TIPP
23
Dem Einsatz von Stylesheets sind auch Grenzen gesetzt, wenn der Nutzer eigene Stylesheets verwendet, die das Aussehen der Seite mit beeinflussen, oder Stylesheets komplett untersagt.16 Im Internet Explorer können Sie beispielsweise über das Menü EXTRAS/INTERNETOPTIONEN und die Schaltfläche EINGABEHILFEN eigene BENUTZERSTYLESHEETS vergeben.17
24
16 Beim Netscape Navigator 4.x ist dies sogar der Fall, wenn der Nutzer JavaScript deaktiviert, da die CSS-Engine direkt mit JavaScript integriert ist. 17 Ein Stylesheet, das den Rahmen von Tabellen oder -Tags vergrößert, kann sehr praktisch sein, um komplexere Layouts von anderen Seiten genauer anzuschauen.
637
Inhalt
23 Dynamisches 14
15
Eine Vielzahl der unter dem Titel »Ajax« bekannten Webanwendungen basiert nicht hauptsächlich auf dem Datenaustausch, sondern auf JavaScript-Effekten wie Drag&Drop und Ein- und Ausblenden von Elementen. Diese Aufgaben waren schon lange Zeit unter dem Namen DHTML bekannt. DHTML steht für Dynamic HTML. Der Begriff Dynamic ist schwammig. Da DHTML kein eigener technologischer Standard ist, hat sich daraus eine Vielzahl von Definitionen gebildet. Übrig geblieben sind zwei Ansätze: ■ ■
16
17
DHTML als Kombination der Technologien HTML, CSS und JavaScript unter Verwendung eines DOM (Document Object Model) DHTML als (clientseitig) programmierte Interaktion auf einer Website, die zumindest mit HTML arbeitet1
18
Die Wahrheit – das heißt die Praxis – liegt wohl irgendwo zwischen den beiden Definitionen, denn es scheint weder vollkommen logisch, dass alle drei Technologien inklusive DOM vorhanden sein müssen, noch ist es besonders sinnvoll, per mailto: versendete Formulareingaben als DHTML zu bezeichnen.
19
Nun aber genug der akademischen Diskussionen. In diesem Kapitel erfahren Sie zunächst Näheres über die Grundlagen von DHTML und dynamischen Anwendungen.
21
Zum besseren Verständnis der Grundlagen ist es hilfreich, in Kapitel 8 »Document Object Model« und Kapitel 14 »Browserunterscheidung« nachzuschlagen, denn dort werden die Grundlagen für DHTML gelegt. In diesem Kapitel finden Sie daher mehrere Verweise auf diese beiden.
20
22 REF
23
Anschließend finden Sie die DHTML-Beispiele nach Anwendungen sortiert, damit Sie direkt erkennen können, was Ihnen in der Praxis nutzt. Die Anwendungsbeispiele sind in folgende Bereiche unterteilt:
■ ■
1
Maus – Interaktionen mit der Maus, aber auch die Wandlung des Mauszeigers gehören in diesen Bereich. Navigation – Wie bessert DHTML optisch eine Navigation auf? Welche Lösungen sind möglich? Animation – DHTML und Bewegung auf der Webseite. Dies schließt beispielsweise Flash aus.
639
ndex
■
24
Dynamisches
23.1
Historie und Grundlagen
DHTML ist ab der vierten Generation des Netscape Navigators und Internet Explorers möglich. Dies schließt ältere Browser natürlich aus. Aber auch bei allen Browsern ab der vierten Generation müssen sehr viele Aspekte beachtet werden. Der Netscape Navigator 4.x hat ein komplett eigenes Objektmodell und für DHTML beispielsweise Layer, die kein anderer Browser, also auch keine spätere Netscape-Version, kennt. Der Internet Explorer verwendet ein Objektmodell, das auf document.all beruht. Erst die neueren Browser ab Internet Explorer 5 und Netscape 6 unterstützen das W3C-DOM.2 Außerdem natürlich alle modernen Browser wie Internet Explorer 7, Mozilla. Auch im Konqueror, seinem Mac OS X-Ableger Safari und in Opera wird das W3C-DOM gut unterstützt. Wenn Sie mit DHTML arbeiten und auch die vierte Generation der Browser unterstützen möchten, müssen Sie also drei Objektmodelle berücksichtigen. Ansonsten ist es sinnvoll, sich nur auf das W3C-DOM zu beschränken. Sollten Sie die vierte Generation noch unterstützen wollen, ist eine effektive Browserunterscheidung notwendig. Im DHTML-Bereich wird hier die Unterscheidung mit Browserobjekten bevorzugt.3 Mit dieser Unterscheidung erfahren Sie sofort, ob ein Browser ein Objekt direkt unterstützt. if (document.all)
prüft beispielsweise, ob der Browser document.all unterstützt. Ein Internet Explorer liefert etwas zurück, wodurch die Bedingung als erfüllt (true) gilt. Andere Browser geben undefined zurück, was für die Bedingung false bedeutet.
HALT
Die Browserunterscheidung mittels navigator-Objekt kann für eine DHTMLSeite unangenehme Konsequenzen haben. Häufig werden nur einige wenige Browser abgeprüft. Neue Browser und »Randbrowser« wie beispielsweise Safari, Konqueror oder Opera, die sich nicht als anderer Browser ausgeben, bleiben außen vor, obwohl sie das W3C-DOM gut unterstützen. Die Beispiele in diesem Kapitel verwenden die Browserunterscheidung mit Browserobjekten. Einige Beispiele funktionieren allerdings nicht in allen Browsern. In diesem Fall finden Sie jeweils Hinweise und Erklärungen. In den nächsten Abschnitten werden die Besonderheiten einiger Browser im Umgang mit DHTML grundlegend erläutert. Dies ist zum Verständnis der folgenden Beispiele hilfreich.
2 3
640
Nähere Informationen zu DOMs erhalten Sie in Kapitel 8 »Document Object Model«. Siehe Abschnitt 14.4 »Browserobjekte«.
Historie und Grundlagen
23.1.1 Netscape 4.x und Layer Aus der Sicht des DHTML-Programmierers ist Netscape 4.x der Albtraum unter den Browsern. Er kocht in allen Bereichen sein eigenes Süppchen. Wie Sie in Kapitel 22 »CSS und JavaScript« nachlesen können, ist auch die CSSUnterstützung nur teilweise vorhanden und unterscheidet sich von den anderen Browsern. Beispielsweise besitzt der Netscape Navigator 4.x kein style-Objekt und verfügt über wesentlich weniger Möglichkeiten, Stile zu ändern.
14 15
Die Ursache dieser Inkompatibilitäten und fehlenden Standardunterstützung liegt hauptsächlich darin, dass Netscape den Browser auf den Markt gebracht hat, bevor viele Konzepte standardisiert wurden. Der Bereich, wo dies am deutlichsten wird, sind Layer. Netscape entwickelte das Tag. Layer waren als frei platzierbare Ebenen gedacht, die auch übereinandergeschichtet werden können. Leider ging dieses Konzept nie in den HTML-Standard ein und tauchte in der nächsten Netscape-Version 6 und auch im Mozilla nicht mehr auf. Für DHTML-Programmierer gibt es den angenehmen Umstand, dass im Navigator 4.x auch -Blöcke als Layer angesehen werden. Sie können auf diese Elemente also direkt mit
16 17 18
document.ID
zugreifen.
19
23.1.2 Internet Explorer 4
20
Der Internet Explorer 4 hat zwar auch ein eigenes Objektmodell, macht es dem Programmierer aber nicht ganz so schwer. Die Kollektion document.all enthält alle Elemente. Mit
21
document.all.ID
greifen Sie einfach auf ein Element zu. Alternativ können Sie
22
document.all["ID"]
verwenden. Auch das style-Objekt ist im Internet Explorer 4 schon vorhanden.
23
23.1.3 Internet Explorer-Spezialitäten
24
Der Internet Explorer besitzt einige eigene Funktionen, die von keinem anderen Browser genutzt werden und für DHTML eingesetzt werden können. Zum einen sind dies Stylesheet-Befehle für Filter wie beispielsweise einen Transparenzfilter und zum anderen so genannte Behavior, also standardisierte Verhalten. Diese Funktionen werden in den folgenden Abschnitten beschrieben.
641
Dynamisches
Filter Der Internet Explorer ab Version 4 besitzt unter Windows (nicht beim Mac!) so genannte Filter. Vielleicht sind Ihnen Filter aus Grafikprogrammen bekannt. Es handelt sich dabei um kleine Hilfsprogramme, die Effekte auf ein Bild anwenden, deren Erstellung von Hand deutlich zeitaufwändiger wäre. Im Internet Explorer sind die Filter ein Teil der Stylesheet-Implementierung.4 Die Anwendung erfolgt mit filter und dem Filternamen. In runden Klammern folgen mögliche Attribute. Beliebiger Text auch für Internet Explorer 4
Ab Internet Explorer 5.5 hat Microsoft eine neue Syntax für Filter eingeführt. Es muss genau angegeben werden, wo der Filter zu finden ist. Dazu folgt nach filter zunächst progid und anschließend der Name der Komponente, die die Filter enthält: Beliebiger Text
Abbildung 23.1: Die Filtereffekte im Browser
Damit bestimmte Filter in einem -Block sichtbar werden, benötigen diese Breiten- und/oder Höhenangaben im Stylesheet. HALT
4
642
Sie sind jedoch kein Bestandteil des Standards. Die CSS-Arbeitsgruppe des W3C hat die Vorschläge von Microsoft zurückgewiesen.
Historie und Grundlagen
Eine Anleitung zu Filtern finden Sie bei Microsoft unter http:// msdn.microsoft.com/workshop/filter/filters.asp. Unter http://msdn. microsoft.com/workshop/filter/reference/reference.asp finden Sie eine Auflistung aller zur Verfügung stehenden Filter.
REF
Der Skriptzugriff auf einen Filter erfolgt über das Objekt filter. In der filters[]-Kollektion kann entweder der Index oder der Name des Filters angegeben werden:
14
document.all.ie4.filters["dropshadow"]5
15
Analog können Sie die Eigenschaften eines Filters ändern: document.all.ie4.filters["dropshadow"].offx = "10";
16
Die neue Syntax ab Internet Explorer 5.5 erfordert etwas mehr Tipparbeit, da der Name mit Komponente angegeben werden muss:
17
document.all.ie55.filters["DXImageTransform.Microsoft.Glow"].color = "blue";
Das folgende Beispiel ändert beim Anklicken der Schaltfläche jeweils eine Eigenschaft eines Filters in der alten und der neuen Syntax: Listing 23.1:
18
Eigenschaften von Filtern ändern (filter.html)
Filter Beliebiger Text auch für Internet Explorer 4 Beliebiger Text Filter ändern
5
19 20 21 22 23 24
Dieser Zugriff lässt sich mit item schreiben, also document.all.ie4.filters.item("dropshadow"). Das Ergebnis ist identisch.
643
Dynamisches
Abbildung 23.2: Die Filter vor (links) und nach (rechts) Anklicken der Schaltfläche
TIPP
Mit der Eigenschaft enable können Sie Filter an- (true oder 1) und abschalten (false oder 0). Damit lassen sich beispielsweise Rollover-Effekte mit Filtern realisieren. Transitions sind mit den Filtern artverwandt. Man könnte sie als Überblendeffekte von einem Element zu Transparenz oder von mehreren Elementen ineinander beschreiben. Der JavaScript-Zugriff erfolgt wie bei Filtern über die ID des Elements mit Transition und anschließend filters. Behavior Die Behaviors des Internet Explorers wurden zuerst in Version 5 für Windows (nicht für den Mac) des Browsers eingeführt. Sie sind (noch) kein Standard, Microsoft hat sie jedoch als Erweiterung von CSS eingereicht.6 Da eines der Ziele von Behaviors in der Trennung von Code und Content besteht, liegt ein Behavior immer in einer externen Datei mit der Dateiendung .htc. Behaviors werden mit dem CSS-Befehl behavior:url(Behavior.htc) verlinkt. In der Behavior-Datei selbst wird ein selbst erstelltes Verhalten von einem -Block eingeschlossen. Innerhalb dieses Blocks kann das Verhalten mit einem Ereignis zugewiesen werden.
Neben eigenen Behaviors bietet der Internet Explorer auch einige vorgefertigte. Standardbehaviors werden mit #default#Name aufgerufen. .Klasse {behavior:url(#default#userdata)} 6
644
Siehe http://www.w3.org/TR/becss.
Historie und Grundlagen
Diese Zeile weist das Standardbehavior userdata einer Stylesheet-Klasse zu. Dieses Verhalten speichert Daten in einem Objekt. Auf eine ausführliche Beschreibung wird hier aufgrund des proprietären Charakters der Behaviors verzichtet. Außerdem handelt es sich hier ja nicht um ein HTML-Buch. Eine ausführliche Anleitung finden Sie unter http:// msdn.microsoft.com/workshop/author/behaviors/overview.asp. Die einzelnen vorgefertigten Behaviors werden unter http://msdn.microsoft.com/ workshop/author/behaviors/reference/reference.asp vorgestellt.
REF
14 15
Mit den ab Internet Explorer 5 unter Windows zur Verfügung stehenden Methoden addBehavior("URL") und removeBehavior("ID") können Sie Verhalten auch mit JavaScript zuweisen bzw. entfernen.
16
TIPP
17
23.1.4 Neuere Ansätze Die Methode document.write() ist nicht besonders flexibel, wenn HTML oder Text dynamisch in eine Webseite geschrieben werden soll. Daher stellt dieser Abschnitt die für DHTML grundlegenden Alternativen vor. Einziger Makel dieser Alternativen ist, dass sie mit dem Netscape Navigator 4.x nicht funktionieren.
18 19
innerHTML innerHTML erlaubt das Einfügen von HTML-Code in ein beliebiges Element, auf das Sie mit der ID zugreifen. innerHTML funktioniert in allen Browsern, außer im Netscape Navigator 4.x.
20
document.getElementById("text1").innerHTML = "Neuer Inhalt";
21
Neben innerHTML gibt es drei weitere Alternativen, die nur im Internet Explorer und in Opera vorhanden sind: ■ ■
■
22
innerText – fügt reinen Text ein und berücksichtigt HTML-Tags nicht. Diese Eigenschaft ist auch im Konqueror vorhanden. outerHTML – schließt zusätzlich zum HTML innerhalb des Elements auch die Tags des Elements selbst ein. So können Sie ein Element komplett austauschen. outerText – liefert den Text eines HTML-Elements. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
innerText
outerHTML
outerText
innerHTML
23 24 Tabelle 23.1: Eigenschaften zum Einfügen von HTML und Text
645
Dynamisches
Das folgende Beispiel fügt einen Text, den der Nutzer in das Textfeld eingibt, am Ende des Absatzes ein. Das Beispiel beinhaltet eine Fallunterscheidung mit dem Browserobjekt document.all. Listing 23.2:
Mit innerHTML einen Absatz hinzufügen (innerhtml.html)
innerHTML
Ein beliebiger Text.
Abbildung 23.3: Der Text wird hinter dem vorherigen Text angefügt.
TIPP
646
Alternativ zu innerHTML können Sie natürlich auch einen neuen ElementKnoten per W3C-DOM erstellen (vergleiche Kapitel 8). Auch auf diesem Weg können Sie Inhalte ausgeben.
Historie und Grundlagen
insertAdjacentHTML und insertAdjacentText Die beiden Methoden insertAdjacentHTML("Position", "Inhalt") und insertAdjacentText("Position", "Text") ermöglichen Ihnen, HTML-Code oder Text direkt an oder vor ein Element zu hängen. Leider sind beide Methoden nur im Internet Explorer und in Opera verfügbar. NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
insertAdjacentHTML
insertAdjacentText
SF/KQ
Tabelle 23.2: insertAdjacentHTML und insertAdjacentText
■ ■ ■
17
BevorBegin – vor Beginn des Elements AfterBegin – nach Beginn des Elements BevorEnd – vor Ende des Elements AfterEnd – nach Ende des Elements
18 19
Das folgende Beispiel ist eine Abwandlung von Listing 23.2. Hier wird der im Textfeld eingegebene Text (mit HTML-Tags) nach dem Textabsatz eingefügt: Listing 23.3:
15 16
Die Position kann folgende vier Werte annehmen, in Relation zum Objekt, bei dem sie angewendet wird: ■
14
20
insertAdjacentHTML in der Praxis (insertAdjacentHTML.html)
insertAdjacentHTML und insertAdjacentText
Ein beliebiger Text.
21 22 23 24
647
Dynamisches
Abbildung 23.4: Der Text wird immer unter dem ersten Absatz eingefügt.
Dynamische Tabellen In neueren Browsern sind auch Tabellen Objekte.7 Dies ermöglicht Ihnen, Tabellenzellen dynamisch zu erstellen und zu löschen. Das folgende Beispiel erzeugt mit den Methoden insertRow(Index) und insertCell(Index) eine neue Reihe und zwei neue Zellen unter den bereits bestehenden. Mit innerHTML werden die Zellen anschließend mit den Eingaben des Nutzers in ein Textfeld und ein mehrzeiliges Textfeld gefüllt. Und schon ist eine einfache, dynamisch gefüllte Link-Liste fertig: Listing 23.4:
Tabellen in JavaScript dynamisch generieren (dynamic_tabellen.html)
Dynamische Tabellen
14 15 16 17 18 Abbildung 23.5: Die Tabelle wird dynamisch gefüllt.
19 20 21 22 23 24
Im Download-Archiv finden Sie unter dynamic_tabellen_link.html im Ordner code\kap23 eine Variante, in der die Links in der Tabelle direkt verlinkt sind.
WWW
649
Dynamisches
Bei der Link-Liste besteht das Problem darin, dass die Daten nicht gespeichert werden. Wenn der Nutzer die Seite also verlässt oder neu lädt, gehen die Links und Beschreibungen verloren. Um dies zu verhindern, gibt es mehrere Lösungsmöglichkeiten: ■ ■ ■
Sie können die Daten aus der Tabelle in einem Cookie speichern (siehe Kapitel 18 »Cookies«). Alternativ lassen sich die Daten in Formularfeldern innerhalb eines versteckten Frames ablegen. Zu guter Letzt können Sie natürlich auch eine serverseitige Programmiersprache verwenden, um die Daten in eine Datenbank oder XMLDatei zu schreiben. In diesem Fall würden Sie das Beispiel in der Praxis vermutlich komplett serverseitig realisieren.
Weitere Methoden und Eigenschaften Neben den beiden hier verwendeten Methoden insertRow() und insertCell() können Sie mit deleteRow(Index) und deleteCell(Index) einzelne Reihen oder Zellen aus einer Tabelle entfernen. Die zugehörigen Kollektionen, die alle Reihen bzw. Zellen enthalten, heißen rows bzw. cells. Folgende Zeile fügt die erste Zelle der ersten Reihe mit der unmittelbar danebenliegenden zusammen: Tabelle.rows[0].cells[0].colSpan = 2;
HALT
Sie sollten das Tabellendesign nur dann ändern, wenn Sie dies genau kontrollieren können. Anderenfalls bricht ein Tabellenlayout sehr schnell in sich zusammen. Die folgende Tabelle gibt eine Übersicht über die Browserkompatibilität der vier Methoden.
Tabelle 23.3: Methoden für table- und trObjekte
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
insertRow()
deleteRow()
insertCell()
deleteCell()
Die Eigenschaften des td-Objekts finden Sie in der folgenden Tabelle. Funktional entsprechen sie den HTML-Attributen. cellIndex ist die Nummer der Zelle in der Reihe.
650
Maus
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
colspan
rowspan
cellIndex
width
height
noWrap
Der Internet Explorer liefert für Tabellen ein weiteres Konzept namens Data Binding. Es handelt sich hierbei um Controls, die Daten aus einer Datenquelle in eine Website einbinden. Das Problem des Data Bindings gegenüber serverseitigen Lösungen mit ASP.NET oder PHP ist nicht nur, dass es lediglich im Internet Explorer funktioniert, sondern auch, dass die rein clientseitige Lösung nicht aktualisiert werden kann, da die Rückkopplung zum Server fehlt. Daher wird hier auf eine detaillierte Darstellung verzichtet.8
Tabelle 23.4: Eigenschaften für das td-Objekt
14 15 16 INFO
17 18 19
23.1.5 Kleine Fallen Die wichtigsten Browserunterschiede – insbesondere der unterschiedliche Zugriff, der aus den verschiedenen DOMs resultiert – kennen Sie nun. Es bleiben kleine »Nickligkeiten«, die immer wieder in verschiedenen Browsern, Browserversionen oder Plattformen entstehen. Beispiele hierzu sind Probleme beim Ermitteln der Mauskoordinaten oder der Werte für visibility im Netscape Navigator 4.x (siehe Abschnitt 23.3.1 »Sichtbare und unsichtbare Elemente«).
20 21 22
Sie werden in diesem Kapitel einigen Fallen begegnen, die jedoch sicherlich nicht das gesamte Spektrum abdecken. Daher besteht bei DHTML mehr als bei anderen JavaScript-Anwendungen der Hauptteil der Arbeit im Testen.
23 23.2 Maus
24
Die Maus als zweites wichtiges Steuerelement des Computers neben der Tastatur bietet einige interessante Möglichkeiten für DHTML. Sie können den Mauszeiger ändern oder auch Elemente per Drag&Drop verschieben.
8
Weiterführende Informationen finden Sie unter http://msdn.microsoft.com/workshop/author/ databind/data_binding.asp.
651
Dynamisches
23.2.1 Mauszeiger Für den Mauszeiger gibt es einen eigenen Stil-Befehl namens cursor, der dessen Änderung ermöglicht. Dieser Stil-Befehl ist ebenfalls eine Eigenschaft des style-Objekts und kann daher mit JavaScript geändert werden. Tabelle 23.5: cursorEigenschaft
NS4.x
cursor
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel ändert den Mauszeiger in eine Hand (Wert hand), wenn der Nutzer über eine Schaltfläche () fährt. Dies ist recht praktisch, da sich der Mauszeiger nicht ändert, wenn das -Element beispielsweise ein onclick-Ereignis besitzt. Verlässt der Nutzer den Button, wird der Mauszeiger auf default, also den Standardwert, zurückgesetzt. Listing 23.5:
Den Mauszeiger ändern (mouse_cursor.html)
Mauscursor Mauszeiger ändern
Abbildung 23.6: Der Mauszeiger ändert sich, wenn der Nutzer über den Button fährt.
652
Maus
Die cursor-Eigenschaft erlaubt auch weitere Mauszeiger. Die folgende Tabelle listet die wichtigsten auf: Zeiger
Beschreibung
pointer hand
pointer steht für den Mauszeiger, der erscheint, wenn der Nutzer über einen Link fährt. Meist ist dies eine zeigende Hand. hand aktiviert denselben Mauszeiger, funktioniert allerdings nicht mit Netscape Navigator 6 und 7 und Mozilla.
auto
Automatische Wahl des Mauszeigers
default
Standardcursor; meist der Pfeil
help
Cursor für die Hilfe; häufig Pfeil mit Fragezeichen
move
Mauszeiger für Bewegung
text
Textcursor zur Texteingabe
wait
Zeiger für Wartephasen; meist die omnipräsente Sanduhr
Tabelle 23.6: Mögliche Mauszeiger der cursorEigenschaft
14 15 16 17
url("Datei") Zeiger besteht aus einer GIF- oder JPEG-Datei. Dies ist in aktuellen Browsern nicht implementiert.a
n-resize ne-resize e-resize … a.
18
Mauszeiger in Form von Richtungspfeilen. Die Buchstabenkombinationen geben jeweils die Richtungen mittels Himmelsrichtungen (Nord = n, Ost = e, Süd = s, West = w) an.
19
Eine Testdatei für neuere Browser finden Sie unter mouse_cursor_bild.html im Verzeichnis code\kap23 im Download-Archiv.
Der Usability ist es wenig zuträglich, dem Nutzer eine Sanduhr, den typischen Mauszeiger für das Warten, anzuzeigen, wenn es nichts zu warten gibt. Daher sollten Sie auf unsinnige Mauszeiger zum falschen Zeitpunkt verzichten.
20 21 HALT
22
23.2.2 Auf den Spuren der Maus
23
Kennen Sie den Effekt, wenn irgendein Objekt dem Mauszeiger folgt oder vor der Maus zurückweicht? Diesen Effekt gibt es in Flash, allerdings ist er durchaus auch mit DHTML möglich. Das Grundprinzip ist dabei ganz einfach: Sie müssen die Mauskoordinaten feststellen und das Objekt anschließend entsprechend positionieren. Wie sich später noch zeigen wird, liegt die Tücke im Detail.
24
Das folgende Beispiel zielt darauf ab, dass ein Objekt direkt über dem Mauszeiger liegt und sich immer mitbewegt. Das Skript besteht aus drei Teilen: ■ ■
einer Funktion, die beim Laden der Seite ausgeführt wird und die für die Ereignisbehandlung zuständig ist, einer Funktion, die die Koordinaten der Maus herausfindet,
653
Dynamisches
■
TIPP
und einer Funktion, die das Bild – eingeschlossen in einem -Block9 – an die Mauskoordinaten bewegt.
Viele JavaScript- und Ajax-Bibliotheken bieten wesentlich ausführlichere Skripte als das hier gezeigte und nehmen Ihnen damit viel Arbeit ab. In diesem Beispiel geht es vor allem um die Grundfunktionalität. Ereignisbehandlung Die Ereignisbehandlung startet mit einer Fallunterscheidung, ob window.Event im Browser vorhanden ist. function start() { if (window.Event) {
Innerhalb der ersten Fallunterscheidung folgt eine zweite, die den Konqueror und sein neues Gegenstück für Mac OS X, den Safari von Apple, aussortiert. Diese beiden Browser kommen nicht mit document.captureEvents() zurecht, sondern benötigen immer window.captureEvents().10 if(navigator.userAgent.indexOf("Konqueror") == -1 && navigator.userAgent.indexOf("Safari") == -1 ) { document.captureEvents(Event.MOUSEMOVE); } else { window.captureEvents(Event.MOUSEMOVE); } } document.onmousemove = koo; }
Mauskoordinaten Die zwei Variable für die Mauskoordinaten wurden bereits vor dem Funktionsblock definiert: var x_koo; var y_koo;
Die Funktion koo() enthält die Zuweisung der Mauskoordinaten zu diesen Variablen für die verschiedenen Browser. Dazu wird an die Funktion als Parameter e das aktuelle Ereignis übergeben. Für Netscape 4.x werden die Mauskoordinaten mit pageX und pageY ausgelesen: if(document.layers) { x_koo = e.pageX; y_koo = e.pageY; }
9
Dies ist für Netscape 4.x notwendig, da er nur Layer positionieren kann (siehe Abschnitt 23.1.1 »Netscape 4.x und Layer«). 10 Nähere Informationen zu Ereignissen erhalten Sie in Kapitel 9 »Ereignisse und Event-Handler«.
654
Maus
Der Internet Explorer verwendet den direkten Zugriff über event und zum Zugriff auf die Koordinaten clientX und clientY. else if (document.all) { x_koo = event.clientX; y_koo = event.clientY; }
14
Für die übrigen Browser erfolgt der Zugriff über e.clientX und e.clientY. else if (document.getElementById) { x_koo = e.clientX; y_koo = e.clientY; }
15
Abschließend wird die Funktion zum Positionieren aufgerufen und die Koordinaten als Parameter übergeben:
16
bew(x_koo, y_koo);
17
Positionieren Die Funktion bew(x_koo, y_koo) weist – für die verschiedenen Browser einzeln – die Koordinaten zu und zieht davon jeweils 30 Pixel ab, damit sich der Ball in der Mitte unter dem Mauszeiger befindet. Der Unterschied zwischen den einzelnen Browsern besteht im Zugriff auf den -Block:11
18 19
if (document.layers) { document.zeiger.left = x_koo - 30; document.zeiger.top = y_koo - 30; } else if (document.all) { document.all.zeiger.style.posLeft = x_koo - 30; document.all.zeiger.style.posTop = y_koo - 30; } else if (document.getElementById) { document.getElementById("zeiger").style.left = x_koo - 30; document.getElementById("zeiger").style.top = y_koo - 30; }
20 21 22
Komplett Nachfolgend sehen Sie den vollständigen Code. Die Funktionsaufrufe und Funktionsnamen12 sind hervorgehoben.
23
Listing 23.6: Ein Bild folgt dem Mauszeiger (mauszeiger.html).
24
Dem Mauszeiger folgen -Block muss vorher absolut positioniert werden. 12 Für die start()-Funktion hat sich in vielen Skripten auch der Name init() eingebürgert. Dieser Name ist allerdings keine Pflicht.
655
Dynamisches
if(navigator.userAgent.indexOf("Konqueror") == -1 && navigator.userAgent.indexOf("Safari") == -1 ) { document.captureEvents(Event.MOUSEMOVE); } else { window.captureEvents(Event.MOUSEMOVE); } } document.onmousemove = koo; } function koo(e) { if(document.layers) { x_koo = e.pageX; y_koo = e.pageY; } else if (document.all) { x_koo = event.clientX; y_koo = event.clientY; } else if (document.getElementById) { x_koo = e.clientX; y_koo = e.clientY; } bew(x_koo, y_koo); } function bew(x_koo, y_koo) { if (document.layers) { document.zeiger.left = x_koo - 30; document.zeiger.top = y_koo - 30; } else if (document.all) { document.all.zeiger.style.posLeft = x_koo - 30; document.all.zeiger.style.posTop = y_koo - 30; } else if (document.getElementById) { document.getElementById("zeiger").style.left = x_koo - 30; document.getElementById("zeiger").style.top = y_koo - 30; } } //-->
TIPP
656
Verwenden Sie als Bild einfach einmal eine GIF-Animation, am besten mit transparenten Bereichen. Dies erzeugt weitere interessante Effekte. Das Skript selbst können Sie verfeinern, indem Sie den Ball beispielsweise von den Wänden zurückprallen oder ihn um den Mauszeiger kreisen lassen. Das Skript wird dann jedoch schnell sehr komplex.
Maus
Abbildung 23.7: Das Bild folgt dem Mauszeiger.
14 15 16 17 18 23.2.3 Drag&Drop
19
Die Königin der Maus-DHTML-Anwendungen ist das Drag&Drop (Ziehen und Loslassen) von Elementen auf der Website.13 Es ist beispielsweise die Grundlage von JavaScript-Spielen.
20
Drag&Drop-Skripte lassen sich auf viele verschiedene Arten erstellen. Der Kern des Ganzen ist aber immer gleich und besteht aus drei Ereignissen: ■ ■
■
21
onmousedown – wenn der Nutzer auf das Element klickt. Der Vorgang geht bis zum Drückpunkt direkt vor dem Loslassen der Maustaste. onmousemove – dient dazu, die Bewegung des Elements abzufangen. Dieses Ereignis soll nur dann genutzt werden, wenn das Element zuvor mit der Maus angeklickt wurde (onmousedown). onmouseup – soll das Drag&Drop beenden.
22 23
Das Bild für dieses Beispiel finden Sie unter bild.gif im Ordner code\kap23 im Download-Archiv.
24 WWW
Auch hier ist das Skript nur ein vereinfachtes Beispiel für die Funktionsweise von Drag&Drop. In den in Teil 4 vorgestellten JavaScript- und Ajax-Bibliotheken finden Sie umfangreiche Drag&Drop-Lösungen, die stetig weiterentwickelt werden.
TIPP
13 Mit JavaScript ist Drag&Drop über Anwendungen hinweg natürlich nicht zu realisieren, da eine Systemprogrammierung erforderlich wäre.
657
Dynamisches
Ereignisbehandlung Den Anfang macht onmousedown. Dieses Ereignis soll abgefangen werden. Daher wird beim onload-Ereignis im -Tag die Funktion start() aufgerufen. Für den Netscape Navigator 4.x und die W3C-konformen Browser wird das Ereignis für den Layer direkt mit captureEvents() abgefangen. Der Internet Explorer benötigt dies nicht. function start() { if (document.captureEvents) { document.captureEvents(Event.MOUSEDOWN); }
HALT
Wenn Sie mit window.Event zu Beginn testen, ob der Browser die Ereignisbehandlung mit captureEvents() unterstützt, hätten Konqueror und Safari ein Problem mit dem Skript, da sie beide nicht document.captureEvents(), sondern window.captureEvents()14 verwenden. Da der captureEvents()Aufruf in diesem Beispiel für die beiden Browser nicht erforderlich ist, ist keine weitere Fallunterscheidung nach Browserversionen notwendig. Sollten Sie eine Fallunterscheidung benötigen, verwenden Sie: if (navigator.userAgent.indexOf("Konqueror") != -1 && navigator.userAgent.indexOf("Safari") != -1 )
oder testen alternativ auf das Browserobjekt: if (window.captureEvents)
Als Nächstes erfolgt der Ereignisaufruf für das jeweilige Element: if (document.layers) { document.ele1.onmousedown = druecken; } else if (document.all) { document.all.ele1.onmousedown = druecken; } else if (document.getElementById) { document.getElementById("ele1").onmousedown = druecken; } }
HALT
Der Ereignisaufruf über das Element startet das Ereignis nur, wenn der Nutzer es anklickt. Leider ist ein Bild immer rechteckig. Bei einem transparenten GIF – wie in diesem Fall – führt also auch ein Anklicken der Ecken zum Drag&Drop. Dies ist insbesondere bei mehreren überlappenden Objekten unangenehm. Um es zu verhindern, müssten Sie mittels einer mathematischen Formel den Kreis festlegen, um nur beim Klick auf Pixel im Kreis das Objekt zu ziehen. Alternativ können Sie den Kreis mit vielen kleinen Rechtecken approximieren. Dadurch wird der Code jedoch ausgesprochen komplex.
14 Dies gilt auch für document.releaseEvents() bzw. im Konqueror/Safari window.releaseEvents().
658
Maus
Drücken Die Funktion »Drücken« trifft zuerst ein paar Vorbereitungen: In die globalen Variablen x_koo und y_koo wird die Differenz zwischen der Mausposition und der linken oberen Ecke des Elements gespeichert. Damit wird festgestellt, welche Stelle des Elements der Nutzer genau anklickt: function druecken(e) {
14
if (document.layers) { document.ele1.offX = e.pageX - document.ele1.pageX; document.ele1.offY = e.pageY - document.ele1.pageY; } else if (document.all) { x_koo = event.clientX - document.all.ele1.style.pixelLeft; y_koo = event.clientY - document.all.ele1.style.pixelTop; } else if (document.getElementById) { x_koo = e.clientX - parseInt(document.getElementById("ele1").style.left); y_koo = e.clientY - parseInt(document.getElementById("ele1").style.top); }
15 16 17
Nun folgen die nächsten Schritte bei den Ereignissen. Für onmousemove und onmouseup ruft das Skript jeweils eine Funktion auf:
18
if (document.captureEvents) document.captureEvents(Event.MOUSEMOVE|Event.MOUSEUP);
19
document.onmousemove = ziehen; document.onmouseup = aufheben;
20
return false; }
Ziehen
21
Das Ziehen besteht darin, das Element immer neu zu positionieren. Dies erfolgt für die verschiedenen Browser unterschiedlich:
22
function ziehen(e) { if (document.layers) { document.ele1.pageX = e.pageX - document.ele1.offX; document.ele1.pageY = e.pageY - document.ele1.offY; } else if (document.all) { document.all.ele1.style.pixelLeft = event.clientX - x_koo; document.all.ele1.style.pixelTop = event.clientY - y_koo; } else if (document.getElementById) { document.getElementById("ele1").style.left = parseInt(e.clientX) - x_koo; document.getElementById("ele1").style.top = parseInt(e.clientY) - y_koo; }
23 24
return false; }
659
Dynamisches
Der Opera vor Version 7 verhält sich im Allgemeinen wie der Internet Explorer, unterstützt jedoch kein posLeft, daher verwendet das Beispiel pixelLeft. Die folgende Tabelle gibt Aufschluss über die Gewinnung von Mauszeigerkoordinaten und die Angabe von Positionierungen in den verschiedenen Browsern. Tabelle 23.7: Unterschiede bei den Positionsangaben für Mauszeiger und -Elemente
Wirkung
Netscape 4.x
IE 4+
W3C-kompatible Opera 5, 6
Mauscursor
Ereignis.pageX Ereignis.pageY
event.clientX event.clientYa
Ereignis. clientX Ereignis. clientX
event.clientX event.clientY
Element
left top
style.posLeft style.posTop style.pixelLeft style.pixelTop
style.left style.top
style.pixelLeft style.pixelTop
a.
Die Langform heißt window.event.clientX.
Aufheben Die Funktion zum Aufheben bei onmouseup ist recht einfach und hebt das Ereignis beim Bewegen der Maus wieder auf: function aufheben() { if (document.releaseEvents) document.releaseEvents(Event.MOUSEMOVE); document.onmousemove = null; return false; }
Komplett Das Beispiel ist nun fertig gestellt und funktioniert in den gängigen Browsern ab Netscape Navigator 4.x, Mozilla, Internet Explorer 4 sowie in den »Exoten« Opera, Konqueror und Safari. Im Folgenden sehen Sie den kompletten Code. Die Funktionsaufrufe sind fett hervorgehoben, um die Zusammenhänge zu verdeutlichen: Listing 23.7:
Drag&Drop für alle Browser (dragdrop.html)
Drag und Drop
Abbildung 23.8: Anklicken …
662
Maus
Abbildung 23.9: … ziehen …
14 15 16 17 18 Abbildung 23.10: … und loslassen
19 20 21 22 23 24
663
Dynamisches
TIPP
Auch bei diesem Beispiel sind einige Erweiterungen möglich und teilweise auch sinnvoll. Zunächst sollten Sie Browser abfangen, die kein JavaScript unterstützen. Des Weiteren können Sie das Beispiel in größere Anwendungen wie Spiele integrieren und das Beispiel um mehrere – beispielsweise in einem Array gespeicherte – Elemente erweitern. Eine weitere Idee ist, den Mauszeiger zu einem Bewegen-Mauszeiger zu ändern (siehe Abschnitt 23.2.1 »Mauszeiger«.
23.2.4 Mehr Drag&Drop Neben dem mit DHTML realisierten Drag&Drop erlauben einige Browser auch ein Drag&Drop von beispielsweise ausgewähltem Text in andere Anwendungen oder in Textfelder auf derselben Seite. Aus der Sicht des JavaScript-Programmierers gibt es hier im Internet Explorer ab Version 5 proprietäre Möglichkeiten: ■
■
REF
Einige Ereignisse wie onDrag, onDragStart und onDragOver, die verschiedene Stadien des Drag&Drop-Vorgangs umfassen. In der Praxis werden sie bisher aufgrund des proprietären Charakters und der beschränkten Einsatzmöglichkeiten kaum verwendet. Das Objekt dataTransfer. Ein solches Objekt tritt auf, wenn ein Element gezogen wird; es korreliert also mit den Ereignissen und kann bei ihnen einfach aufgerufen werden. Dieses Objekt ermöglicht Ihnen, über die Eigenschaft dropEffect zu steuern, wie der Cursor aussehen soll. Die Eigenschaft effectAllowed legt fest, was mit den übermittelten Daten geschehen darf. Einige Methoden erlauben den Zugriff auf die Daten des Objekts.
Da die Technologie auf den Internet Explorer ab Version 5 beschränkt ist, wird sie hier nicht ausführlicher dargestellt. Informationen erhalten Sie unter http://msdn.microsoft.com/workshop/author/datatransfer/overview.asp.
23.3 Navigation Die Navigation steht für den Webdesigner und den Webprogrammierer im Vordergrund. Sie muss gut aussehen, einfach anzuwenden sein – Stichwort »Usability« – und dem Nutzer alle Informationen zur Verfügung stellen. DHTML hilft hier nicht nur mit interessanten optischen Effekten, sondern kann die Navigation auch anders aufteilen.
664
Navigation
23.3.1 Sichtbare und unsichtbare Elemente Die Grundlage aller Navigationselemente ist das Sichtbar- und Unsichtbarmachen von Elementen. Als Elemente sollten – bei browserübergreifender Programmierung – nur die -Blöcke verwendet werden, da der Netscape Navigator 4.x sie als Layer erkennt und den Zugriff darauf zulässt.15
14
Das folgende Beispiel ist eine kleine und sehr einfach gestaltete Werbeseite für dieses Buch.16 Für jeden Buchteil gibt es einen Hilfetext, der eingeblendet wird, wenn der Nutzer mit der Maus über den Link fährt.
15
Das Beispiel besteht aus drei Funktionen: ■
Die Funktion check(kasten) übernimmt aus den onmouseover-Ereignissen als Parameter die Nummer des Elements, das überfahren wird. Auf diese Weise können Sie die Funktion für viele Hilfetexte flexibel verwenden. Anschließend realisiert die Funktion den Zugriff auf das jeweilige Objekt. Abschließend wird über eine Testvariable (test) festgestellt, ob ein Hilfetext aktuell eingefügt wurde. Dementsprechend wird die Funktion einblenden() oder ausblenden() aufgerufen und die Referenz auf das aktuelle Element übergeben.
Besondere Schwierigkeiten bereitet das dynamische Zusammensetzen des Namens div mit der Nummer des -Elements. In anderen Browsern kann eine Variable mit der ID eingefügt werden, nur im Netscape Navigator 4.x muss die ID direkt angegeben werden. Dies erfordert eine aufwändigere Fallunterscheidung:
16 17 18 19
INFO
20
else if (document.layers) { if (kasten == "div1") z = document.div1; else if (kasten == "div2") z = document.div2; else if (kasten == "div3") z = document.div3; } ■
■
21 22 23
Die Funktion einblenden(obj) übernimmt über den Parameter die Objektreferenz auf den -Block mit dem Hilfetext und blendet ihn mit visibility ein. Die Funktion ausblenden(obj) blendet die Sichtbarkeit wieder aus.
24
Hier der vollständige Code: Listing 23.8: Flexible Hilfetexte mit DHTML (hilfe.html) Ajax-Kompendium 15 Siehe Abschnitt 23.1.1 »Netscape 4.x und Layer«. 16 Da Sie dieses Buch bereits gekauft haben, ist dies noch nicht einmal Schleichwerbung.
665
Dynamisches
body, td {font-family: Arial,sans-serif} a:link {color: orange} div.kasten {background-color: orange; visibility: hidden; border-width: 2pt; border-style: solid; padding: 5px; border-color: black} Ajax-Kompendium
Dieses Buch behandelt alle wichtigen Aspekte von Ajax und JavaScript und hilft, dynamische Anwendungen für das Web zu realisieren.
Die Teile des Buchs: |
666
Navigation
Teil I | JavaScript |
Teil II | Ajax |
Teil III | Anwendungen |
Die Grundlagen und fortgeschritteneProgrammierung mit Ajax-Grundlagen und -Einsatz. JavaScript-Anwendungen für den Alltag.
14 15 16 200px;
17
JavaScript. 235px;
18
270px;
19 20 Abbildung 23.11: Der Hilfetext im orangefarbenen Kasten
21 22 23 24
667
Dynamisches
Browserschwierigkeiten Das Sichtbar- und Unsichtbarmachen von Elementen hat einige Haken. Das erste Problem – das fehlende style-Objekt im Netscape Navigator 4.x – lösen Sie am besten, indem Sie das Objekt direkt in den Zugriff integrieren, wie dies auch in den bisherigen Beispielen erfolgte (siehe Listing 23.8). Ein weiteres Problem entsteht, wenn Sie testen möchten, ob ein Element bereits sichtbar ist. Alle Browser liefern visible für sichtbar und hidden für unsichtbar zurück. Alle Browser? Der Netscape Navigator tanzt wieder aus der Reihe. Hier steht show für sichtbar und hide für unsichtbar. Eine Überprüfung für alle Browser muss also beide Browser berücksichtigen: if (absatz.visibility == "visible" || absatz.visibility == "show")
Sie finden ein Beispielskript mit dem Namen dhtml.html im Ordner code\kap23 im Download-Archiv. WWW
23.3.2 zIndex Wenn sich mehrere positionierte Elemente überlappen, entscheidet die Reihenfolge im HTML-Code darüber, welches Element höher liegt. Wollen Sie dagegen die Position selbst festlegen, können Sie in CSS den z-index als ganze Zahl angeben. Das Element mit dem größeren z-index befindet sich oben. Zum z-index bietet JavaScript eine eigene Eigenschaft, die in der JavaScriptSyntax zIndex geschrieben wird.17 Sie ist in den DHTML-fähigen Browsern vorhanden. Tabelle 23.8: zIndex zIndex
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Das folgende Beispiel verwendet zIndex, um beim Anklicken des Buttons die Überlagerung der beiden Bilder zu ändern. Die Prüfung und Änderung von zIndex ist fett hervorgehoben. Der Zugriff auf die Bilder, die in Blöcke eingebaut sind, erfolgt wie gewöhnlich. Listing 23.9: Den zIndex dynamisch verändern (zindex.html) zIndex
18 19 20 Abbildung 23.12: Zuerst ist das eine (links) und dann das andere Buch im Vordergrund (rechts).
21 22 23 24
669
Dynamisches
Die Positionierung von Formularelementen ist im Netscape Navigator 4.x und in älteren Versionen des Opera noch nicht möglich. INFO
23.3.3 Elemente permanent sichtbar machen Eine häufige Aufgabe sowohl in der Navigation als auch in der Werbung besteht darin, dass ein Element der Seite erhalten bleiben soll, auch wenn der Nutzer in der Seite nach unten oder zur Seite scrollt. Die Positionierung mit CSS hat für dieses Problem eine Lösung: Sie können Elemente mit fixed positionieren, sodass sie auch beim Scrollen in ihrer Position relativ zum Browserfenster bleiben.
Das Problem bei dieser Lösung ist, dass weder Netscape Navigator 4.x noch Internet Explorer bis inklusive Version 6 fixed unterstützen. Tabelle 23.9: fixed
NS4.x
fixed a.
M/FF
IE4
IE5 ()a
IE5.5
IE6
IE7
Op
SF/KQ
Beim Mac.
JavaScript bietet hier einen Ausweg. Sie müssen lediglich feststellen, wie weit die Seite gescrollt wurde, und dann die Position des Elements anpassen. Damit die Überprüfung permanent erfolgt, ruft sich die zugehörige Funktion permanent() mit setTimeout() immer wieder selbst auf. Zunächst wird beim Laden der Seite die Funktion start() aufgerufen. In dieser Funktion wird zuerst die Position des Elements ausgelesen, das permanent angezeigt werden soll. Das Skript führt dabei eine einfache Browserunterscheidung mit Browserobjekten durch. function start() { if (document.layers) { x_pos = document.bild1.left; y_pos = document.bild1.top; } else if (document.all) { x_pos = document.all.bild1.style.pixelLeft; y_pos = document.all.bild1.style.pixelTop; } else if (document.getElementById) { document.getElementById("bild1").style.position = "fixed"; return; } permanent(); }
670
Navigation
Für Browser, die document.getElementById() unterstützen, wird die Positionierung direkt auf fixed gesetzt. Dieser Weg ist natürlich eine Abkürzung. Sie könnten auch für diese Browser die Position mit JavaScript setzen. Allerdings wird die fixierte Positionierung mit CSS im Browser wesentlich sanfter gerendert und sieht daher einfach flüssiger aus, wenn der Nutzer scrollt. Da auch der Internet Explorer document.getElementById() unterstützt, fixed jedoch nicht, müssen Sie die Überprüfung von document.all unbedingt vor der auf document.getElementById() durchführen.
14 HALT
15 Nachdem die normale Position des Elements festgestellt wurde, dient die Funktion permanent() dazu, die Scrollposition festzustellen:
16
function permanent() { if(document.layers) { var x_scroll = window.pageXOffset; var y_scroll = window.pageYOffset; } else if (document.all) { var x_scroll = document.body.scrollLeft; var y_scroll = document.body.scrollTop; }
17 18
Das Feststellen der Scrollposition ist – wie in diesem Codefragment zu sehen – von Browser zu Browser unterschiedlich. Die folgende Tabelle gibt eine Übersicht darüber, wie welcher Browser auf die Scrollposition zugreift.18 Netscape 4.x
IE 4+
Navigator 6+, Mozilla
Opera 7+ Konqueror/Safari
window.pageXOffset window.pageYOffset
body.scrollLeft body.scrollTopa
window.scrollX window.scrollY
19 Tabelle 23.10: Scrollposition in verschiedenen Browsern feststellen
20 21
Für die Scrollposition in einem Frame oder einem anderen Objekt referenzieren Sie einfach auf dieses Objekt. Ab Internet Explorer 5 liefert diese Eigenschaft für nahezu jedes Objekt einen Wert.
22
Im zweiten Schritt ruft das Skript die Funktion setzen() auf, die das Element anhand der Scrollposition neu setzen soll. Anschließend wird die Funktion permanent() mit setTimeout() wieder aufgerufen, um die Scrollposition zu aktualisieren.
23
a.
24
setzen(x_scroll, y_scroll); setTimeout("permanent()", 50); }
18 Der Zugriff für Opera/Konqueror/Safari und Navigator/Mozilla wird in diesem Beispiel nicht benötigt, da in der Funktion start() für diese Browser die Positionierungsmethode auf fixed geändert wurde.
671
Dynamisches
Die Funktion setzen() verwendet die Scrollposition und die Ausgangsposition des Elements und errechnet daraus die neue Position: function setzen(x_scroll, y_scroll) { if (document.layers) { document.bild1.left = parseInt(x_pos) + parseInt(x_scroll); document.bild1.top = parseInt(y_pos) + parseInt(y_scroll); } else if (document.all) { document.all.bild1.style.pixelLeft = x_pos + x_scroll; document.all.bild1.style.pixelTop = y_pos + y_scroll; } }
Nachfolgend sehen Sie den vollständigen Code. Funktionsaufrufe und Namen sind fett hervorgehoben: Listing 23.10: Scrollen mit JavaScript verhindern (logo_permanent_js.html) Permanent
15 16 Abbildung 23.13: Nun scrollt das Logo auch im Internet Explorer mit.
17 18 19 20 21 22 23
Die Geschwindigkeit, mit der setTimeout() die Funktion wieder aufruft, entscheidet, wie schnell gescrollt wird. Zum Testen sollten Sie diese hochsetzen, später können Sie dann die richtige Geschwindigkeit wählen.
24 TIPP
673
Dynamisches
Scrollen Das Feststellen der Scrollposition ist nicht die einzige dynamische Möglichkeit beim Scrollen. Es gibt auch drei Methoden, die ein dynamisches Setzen der Scrollposition per JavaScript erlauben: ■
window.scroll(x, y) – scrollt das Dokument absolut von der linken oberen Ecke nach links (x) und unten (y). Folgende Zeile setzt also die Scrollposition wieder zurück: window.scroll(0, 0);
■
window.scrollBy(dx, dy) – scrollt das Dokument relativ zur aktuellen Position nach links (dx) und unten (dy). Die folgende Zeile scrollt 20 Pixel nach rechts und 40 Pixel nach oben: window.scrollBy(20, -40);
■
window.scrollTo(x, y) – scrollt das Dokument absolut. Ersetzt die Methode window.scroll() ab den Browsern der vierten Generation. Die folgende Zeile scrollt horizontal auf die Anfangsposition und vertikal auf 100 Pixel: window.scrollTo(0, 100);
scroll() existiert als Einzige der drei Funktionen bereits im Netscape 3, die anderen beiden Funktionen sind erst ab der vierten Generation enthalten. Tabelle 23.11: Methoden zum Scrollen
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
scroll()
scrollBy()
scrollTo()
23.4 Animation Eine der spannendsten Anwendungen von DHTML ist die Animation von Elementen. Vielleicht haben Sie schon einmal ein Werbebanner gesehen, das über die Webseite fliegt. Das war vermutlich DHTML. In diesem Abschnitt finden Sie ein umfangreicheres Beispiel mit einer zufallsgesteuerten Animation. Darüber hinaus werden die einzelnen Objekte dynamisch erstellt. Das Beispiel soll fallenden Schnee auf einer Webseite darstellen. Ein solcher Effekt ist normalerweise eine Domäne von Flash, aber auch DHTML kann das. Das Prinzip ist recht einfach: ■
674
Das Skript gibt dynamisch -Blöcke mit ebenfalls dynamisch vergebenen IDs und per Zufall gewählter Position aus, die ein Bild mit einer Schneeflocke enthalten.
Animation
Im Download-Archiv finden Sie das Bild mit dem Namen flocke.gif im Ordner code\kap23. Wenn Sie andere Farben bevorzugen, finden Sie dort auch die original Photoshop-Datei flocke.psd. ■
■
WWW
Nachdem die Schneeflocken über das gesamte Fenster zufallsgesteuert verteilt sind, wird eine Funktion, die die Flocken bewegen soll, mit setInterval() in regelmäßigen Abständen aufgerufen. Die Funktion zum Bewegen der Flocken erhöht bei jedem Aufruf die y-Koordinate um 5 Pixel, damit die Flocken nach unten fallen.
14 15 Abbildung 23.14: Schneetreiben im Browser
16 17 18 19 20
Auch viele JavaScript- und Ajax-Bibliotheken bieten Animationsfunktionalitäten. Sie sollten allerdings nicht unterschätzen, dass die Animationen je nach Performance des jeweiligen Rechners unter Umständen sehr ruckelig aussehen können. JavaScript erreicht hier selten das Level von Flash-Animationen.
21 TIPP
22 23
23.4.1 Vorbereitungen Die Vorbereitungen bestehen – wie meistens – aus Variablendeklarationen.19 Die Variable name soll später die IDs der einzelnen Schneeflocken-Blöcke aufnehmen. Die Variable ele enthält die Ausgabe und wird bereits als leere String-Variable vordefiniert.
24
19 Wenn Sie ein eigenes Skript programmieren, benötigen Sie häufig währenddessen zusätzliche globale Variablen. Deklarieren Sie diese dann einfach am Anfang und verwenden Sie sie später. So schaffen Sie auch während des kreativen Prozesses Ordnung im Skript. Denn um ehrlich zu sein, wenn sie einmal laufen, ändert ein Programmierer seine Skripte eher nicht mehr ab.
675
Dynamisches
Die beiden Variablen f_hoehe und f_breite enthalten die Höhe und Breite des Browserfensters, wobei eine Fallunterscheidung feststellt, wie die Maße bei verschiedenen Browsern gemessen werden. Von der resultierenden Höhe zieht das Skript noch einmal 30 Pixel ab, da die Schneeflocke selbst ebenfalls eine Höhe hat. var name; var ele = ""; if (window.innerHeight) { var f_hoehe = window.innerHeight - 30; var f_breite = window.innerWidth - 30; } else if (document.body.clientHeight) { var f_hoehe = document.body.clientHeight - 30; var f_breite = document.body.clientWidth - 30; }
Die Messung der Höhe des Browserfensterinhalts ist in den verschiedenen Browsern unterschiedlich: ■ ■
INFO
Netscape 4.x, 6 und 7, Mozilla, Konqueror/Safari und Opera verwenden window.innerHeight und window.innerWidth. Der Internet Explorer verwendet stattdessen document.body. clientHeight und document.body.clientWidth.
Sollten die Schneeflocken über die Höhe des Browserfensters hinauslaufen, werden in unschöner Abfolge immer wieder die Scrollbalken eingeblendet. Ein ausreichender Spielraum ist also notwendig.
23.4.2 Dynamische Ausgabe Die dynamische Ausgabe besteht aus einer Schleife, die mittels der Zählervariablen i von 0 bis 20 läuft. i dient dazu, die flexible ID des -Blocks festzulegen. Die Position der 20 auf diese Weise generierten -Blöcke wird mittels einer Zufallszahl und der Höhe des Browserfensters bestimmt.20 for (var i = 0; i
17 18 19 20 21 22 23 24
679
Dynamisches
23.4.6 Erweiterungen Wenn Sie die Grundlagen von DHTML verstanden haben, können Sie die Beispiele nahezu beliebig erweitern und verfeinern. Die Betonung liegt auf »verstanden«, denn es ist selten ausreichend, sich einfach ein hübsches Skript aus dem Netz zu holen und es anzupassen. Wenn Sie nicht wissen, was das Skript macht, verlieren Sie sich schnell in Details und brauchen schließlich mehr Zeit, als Sie bei der Einarbeitung in die Grundlagen benötigt hätten. Dieses Animationsbeispiel lässt sich auch sehr vielseitig erweitern. Einige Erweiterungsvorschläge und -anregungen finden Sie hier: ■
Sie können das Skript in eine beliebige Seite einbauen. Sie müssen dabei lediglich darauf achten, dass die Schneeflocken über allen anderen Elementen liegen, also einen höheren z-index haben. Dazu erhöhen Sie einfach den z-index bei der dynamischen Ausgabe. ele += "px\; left:" + Math.round(Math.random()*(f_breite)) + "px\; z-index:" + (i+5) + "\' ";
Abbildung 23.15: Das Schneegestöber wurde in eine Seite eingebaut.
Im Download-Archiv finden Sie unter zufall_animation_inseite.html ein Beispiel, bei dem das Schneegestöber in Listing 23.8 eingebaut wurde. WWW ■
680
Sie können auch bei der Bewegung einen Zufall mit einbauen und die Schneeflocken in unterschiedlichen Geschwindigkeiten nach unten fallen lassen.
Animation
Ein Beispiel befindet sich unter zufall_animation_doppelt.html im Download-Archiv. Dort wurde eine weitere Zufallsvariable namens bew eingeführt, die für jede Schneeflocke eine Fallgeschwindigkeit angibt. ■
■
■
WWW
Sie können das Schneegestöber mit setTimeout() nach einer festgelegten Zeitspanne beenden. Dazu müssen Sie setInterval() allerdings einer Variablen zugewiesen haben.23 Wenn Sie die Flocken schon vorher als -Blöcke einbauen, können Sie sie dann nur noch platzieren. Diese Methode sorgt vor allem im Netscape Navigator 4.x für ein besseres Ergebnis. Die Animation muss natürlich nicht von oben nach unten laufen. Sie könnte auch von links nach rechts (x-Koordinate) oder schräg (x- und y-Koordinate) verlaufen. Auch die lineare Bewegung ist kein Muss. Verwenden Sie stattdessen beispielsweise eine Bewegung, die mit trigonometrischen Funktionen, beispielsweise Sinus oder Kosinus, ausgerechnet wird.
14 15 16 17 18 19 20 21 22 23 24
23 Mehr zu diesem Thema erfahren Sie in Kapitel 15 »Bilder«.
681
Inhalt
24 Multimedia, Java etc. 14
15
Bis jetzt stand JavaScript im Zentrum der Aufmerksamkeit. Das soll zwar auch in diesem Kapitel so bleiben. Dazu gesellt sich allerdings noch ein zweiter wichtiger Kommunikationspartner.
16
Den Anfang macht Java. Java-Applets sind zwar lange nicht mehr so populär wie vor einigen Jahren, es bleibt dennoch interessant zu sehen, wie JavaScript auf Java-Klassen und -Methoden zugreifen und damit die eigenen Möglichkeiten erweitern kann.
17
Die Steuerung von Sound und Multimedia ist immer eine Steuerung von Plugins in den Browsern, die für das Abspielen solcher Dateien notwendig sind. Diese Steuerung erlaubt zwar einiges, unterscheidet sich aber in den Browsern und auf verschiedenen Betriebssystemen stark.
18
19
Zum Schluss sehen Sie, wie Flash mit JavaScript interagieren kann. Nicht nur, dass die Skriptsprache von Flash, ActionScript, ebenfalls an ECMAScript angelehnt ist, es gibt auch Kommunikationsmöglichkeiten zwischen dem Flash Player, also ActionScript, und JavaScript im Browser.
24.1
20
21
Java
Serverseitig, im Großrechnerbereich und bei Firmenkunden ist Java, die Programmiersprache der Firma Sun, eine Institution. Clientseitig sieht das ein wenig anders aus: Java-Applets benötigen eine Java Virtual Machine. Diese ist zwar bei vielen Plattformen und Betriebssystemen dabei1, allerdings sind die kleinen Applet-Fenster im Browserfenster nicht sehr flexibel und die Performance ist manchmal nicht so gut. Die Performance-Schwäche von gerade älteren Java-Versionen hat einen einfachen technischen Grund: Der Java-Code muss von der Virtual Machine noch zur Laufzeit kompiliert werden. Dieser Vorgang kostet einige Zeit. Dennoch sind Java-Applets für manche besondere Anwendungen durchaus eine Überlegung wert.
22
23
24
Für JavaScript stehen Ihnen drei Kommunikationsmöglichkeiten mit Java zur Verfügung:2
2
Eine Ausnahme sind hier beispielsweise neuere Windows-Versionen, da Sun und Microsoft diesbezüglich schon länger in einem Rechtsstreit liegen. Ausgenommen ist hier serverseitiges Java, beispielsweise in Form von Enterprise Java Beans.
683
ndex
1
Multimedia, Java etc.
■
■ ■
Sie können Java-Klassen direkt aus JavaScript aufrufen. Dies funktioniert über die Technologie Liveconnect, die jedoch nur der Netscape Navigator und der Mozilla unterstützen. Sie haben die Möglichkeit, per JavaScript auf Methoden in Java-Applets zuzugreifen. Sie können aus Java auf JavaScript-Objekte, beispielsweise das Browserfenster, zugreifen.
24.1.1 Liveconnect Liveconnect, vollständig integriert im Netscape Navigator 4.x, erlaubt den Zugriff auf Java-Klassen, die direkt in den Browser integriert sind. Der Zugriff erfolgt immer über die Basisklasse Packages.java, die in Kurzform auch java geschrieben werden kann.
INFO
Liveconnect ist beschränkt auf den Netscape Navigator und Mozilla. Version 4.6 hat kein Liveconnect integriert, in Version 6 des Browsers gibt es keine Implementierung. Sie wurde mit der Unterversion 6.1 nachgeholt. Das folgende Beispiel zeigt, wie der Konstruktor new ein eigenes Java-Fenster als Objekt erzeugt: Listing 24.1:
Auf Java ohne Applet zugreifen (java_ohne_applet.html)
Java ohne Applet Java-Applet-Fenster öffnen
TIPP
Manche Klassen erlauben nur den Zugriff über signierte Skripte.3 Dazu gehört java.io.file, das den Zugriff auf lokale Dateien ermöglicht. Hierzu ist das Privileg UniversalFileAccess notwendig.
3
684
Siehe Kapitel 30 »Sicherheit«.
Java
Abbildung 24.1: Das Java-Fenster
14 15 16 17 18
24.1.2 Zugriff auf Java-Applets Der zweite Kommunikationsweg zwischen JavaScript und Java ist der Zugriff von JavaScript auf Methoden des Java-Applets. Liveconnect und ActiveX heißen hier die entsprechenden Technologien von Netscape bzw. Internet Explorer. Dankenswerterweise funktionieren beide sehr ähnlich, deswegen ist keine Browserunterscheidung notwendig.
19 20
Ein Applet wird mit dem -Tag in den Code eingebunden: Kein Java vorhanden
21
Verwenden Sie ein Mini-Java-Applet mit einer Größe von 1 * 1 Pixel, wenn Sie Java-Klassen und -Methoden ohne ein sichtbares Applet auf der Seite benötigen.
22 TIPP
23
Der Zugriff darauf erfolgt entweder über die applets-Kollektion und den Index:
24
document.applets[0]
Oder über die Kollektion und den Namen: document.applets["Applet1"]
Oder direkt über den Applet-Namen: document.Applet1
Das eigentlich Interessante ist der Zugriff auf Methoden innerhalb des Applets: Mit document.Applet1.Methode();
685
Multimedia, Java etc.
erhalten Sie Zugriff auf alle Methoden des Applets, die als public definiert wurden. Dies sieht in Java folgendermaßen aus: public class { public void Methode() { Anweisungen; } }
Beim Methodenaufruf können durchaus auch Parameter übergeben werden: document.Applet1.Methode(Parameter1, Parameter2);
HALT
Bei Parametern erfolgt eine (automatische) Datentyp-Umwandlung, die in der Praxis meist problemlos abläuft: JavaScript-Objekte werden in JSObjectObjekte umgewandelt, Zahlen (Datentyp Number) in Float. Java-Applets steuern Ein Java-Applet muss kompiliert werden. Dazu benötigen Sie das Java SDK. Im Folgenden sehen Sie ein Beispiel aus dem Java Development Kit. Sie finden es im Programmordner (meist jdk1.1.8) unter demo/clock. Die Uhr hat den Namen clock2.class.4 .class ist die Dateiendung für ein kompiliertes Java-Applet. Der Quellcode ist ebenfalls vorhanden und hat immer die Endung .java. Natürlich können Sie auch auf ein eigenes Java-Applet zurückgreifen, dessen öffentliche Methoden Sie kennen.
INFO
Die Uhr besitzt zwei öffentliche Methoden start() und stop(), deren Namen schon ihre Aufgabe verraten: Sie lassen die Uhr laufen und halten sie an. Die Verwendung dieser Methoden mit JavaScript ist also unproblematisch: Listing 24.2:
Ein Java-Applet steuern (java_applet.html)
Java-Applet steuern Uhr starten Uhr stoppen
Java-Unterstützung
14
Mit dem Zugriff auf Java-Applets gibt es zwei Probleme: ■
■
Die Frage, ob eine Java Virtual Machine vorhanden ist. Die Java-Unterstützung prüft die Funktion navigator.javaEnabled(). Sie kann also zu einer Fallunterscheidung dienen, liefert allerdings nicht die Versionsnummer, sondern nur einen Boolean. Das zweite Problem ist, dass Methodenaufrufe ins Leere gehen, wenn das Applet noch nicht geladen ist. Auch dies kann über eine Prüfung abgefangen werden, denn das Applet liefert den Wert 0, wenn es noch nicht vollständig geladen ist. Folgende Zeile dient zur Überprüfung:
15 16 17
if(document.Appletname != null) document.Appletname.Methode();
18 24.1.3 Java zu JavaScript Der andere Weg von Java zu JavaScript bedarf ebenfalls einiger vorbereitender Schritte:
19
Java benötigt zusätzliche Pakete, um auf JavaScript zuzugreifen. Diese Pakete müssen Sie in Java importieren:
20
■
import netscape.javascript.*; ■
Schwieriger ist es, die Pakete dem Compiler bekannt zu machen. Es gibt hier mehrere Wege, wovon nur einer beschrieben werden soll: 1. Die zwei benötigten Klassen netscape.javascript.JSObject und netscape.javascript.JSException werden beispielsweise beim Netscape Navigator 4.x mitgeliefert. Sie sind im Paket java40.jar versteckt. 2. Dieses Paket muss dem Compiler beim Kompiliervorgang angegeben werden. Am einfachsten erfolgt das mit dem classpath. 3. Nehmen Sie beispielsweise an, Sie hätten das Paket in das Unterverzeichnis lib der JDK (jdk1.1.8) gelegt und die Datei befände sich im Bin-Verzeichnis:5
21 22 23 24
javac –classpath ..\lib\java40.jar ausgabe.java
Sollten Sie einen anderen Compiler oder eine andere Konfiguration verwenden, unterscheiden sich natürlich die Pfade. Egal, wo Sie die Bibliotheken für classpath abgelegt haben, der Browser findet sie beim Anwenden des Applets, wenn sie beim Browser mitgeliefert werden. 5
HALT
Dies dient der Vereinfachung, normalerweise haben Sie längere Pfadangaben.
687
Multimedia, Java etc.
■
Das Java-Applet benötigt die Erlaubnis für den Skriptzugriff. Dieser wird über das Attribut mayscript des -Tags gesteuert. Kein Java vorhanden
Applet und Einbau Ein einfaches Beispiel verdeutlicht die Java-zu-JavaScript-Kommunikation. Das Herzstück ist die JSObject-Klasse in Java. Sie erlaubt den Zugriff auf alle JavaScript-Objekte. Der erste Schritt dazu ist immer die Methode getWindow(Applet), die das übergeordnete window-Objekt des Java-Applets liefert. Mit der Methode eval("String") kann dann JavaScript-Code ausgeführt werden. Im Beispiel wird sie verwendet, um eine einfache Ausgabe mit alert() zu starten. Mit der Funktion getMember(Eigenschaft) haben Sie die Möglichkeit, auf Eigenschaften von Objekten zuzugreifen. Folgende zwei Zeilen realisieren in Java den Zugriff auf ein Eingabefeld im HTML-Formular: JSOject feld = fenster.eval("document.formular.eingabe"); String inhalt = (String) feld.getMember("value");
Hier der vollständige Code des Applets: Listing 24.3: import import import import
Ausgabe mit JSObject (ausgabe.java)
java.awt.*; java.lang.*; java.applet.Applet; netscape.javascript.*;
public class ausgabe extends java.applet.Applet { public void init() { JSObject fenster = JSObject.getWindow(this); fenster.eval("alert('Hallo von Java!')"); } }
WWW
Das Applet müssen Sie nun kompilieren. Sollte dies nach der Anleitung im letzten Abschnitt nicht klappen, finden Sie die kompilierte Datei mit dem Namen ausgabe.class im Ordner code\kap24 im Download-Archiv. Im nächsten Schritt bauen Sie das Applet in die Seite ein. Vergessen Sie dabei nicht das Attribut mayscript im -Tag: Listing 24.4: Von Java zu JavaScript (javascript_java.html) Java zu JavaScript
688
Plugins
Abbildung 24.2: Das Java-Applet meldet sich in JavaScript.
14 Die Datentyp-Umwandlung von Java in JavaScript ist unter Umständen schwieriger als der umgekehrte Weg. Beispielsweise steht der Java-Datentyp long für einen 64-Bit-Integer, der sich in JavaScript aber nur mit 32 Bit abspeichern lässt. Auch die Rückumwandlung von Arrays und Objekten kann im Einzelfall Probleme bereiten.
15 HALT
16 17
24.2 Plugins Die Sound- und Videosteuerung mit JavaScript ist vor allem eines: eine Plugin-Kontrolle. Skripte haben nur direkten Zugriff auf die in Browsern integrierten Plugins. Hier gibt es, wie Sie wohl schon vermutet haben, mehrere Ansätze: ■ ■
■ ■
18 19
Netscape LiveAudio. Ein Netscape-Plugin, das skriptfähig ist. Leider wird es bei vielen Browserversionen nicht mehr mitinstalliert. Microsoft Windows Media Player. Beim Media Player finden Sie leichte Unterschiede zwischen Version 6.4 und höheren Versionen. Für Windows 95 gibt es zusätzlich die ActiveX-Komponente ActiveMovie. Apples QuickTime ab Version 4.1 bietet ebenfalls JavaScript-Unterstützung, allerdings nur eingeschränkt. RealPlayer G2 und höher. Er bietet – als erster RealPlayer – volle Steuerbarkeit über JavaScript, die sogar Eingriffe in das Aussehen der Oberfläche des Players erlaubt.
20 21 22
Die Aufgaben des JavaScript-Programmierers sind nun vielfältig: Er muss die Einbindung beherrschen und erkennen, ob das richtige Plugin vorhanden ist, um es dann über JavaScript zu steuern. Aufgrund der vier verschiedenen Ansätze haben wir folgende Lösung gewählt: Netscape LiveAudio und der Windows Media Player werden zusammen in einem Skript ausgewertet und jeweils verwendet. Apple QuickTime und RealPlayer stehen als Plugin für beide Browser zur Verfügung und werden deswegen einzeln abgehandelt.
23 24
24.2.1 In die Website einbinden Der erste Schritt beim Einbinden von Multimediadateien ist die Einbindung in HTML. Zu diesem Zweck besaß der Netscape Navigator lange Zeit das -Tag, das es aber nie in den W3C-Standard geschafft hat. Microsoft
689
Multimedia, Java etc.
hat stattdessen das -Tag vorgeschlagen und neben seit dem Internet Explorer 4 implementiert. Tabelle 24.1: und
NS4.x
M/FF
IE4
IE5
IE5.5
IE6
IE7
Op
SF/KQ
Um Netscape 4.x-Nutzer nicht auszuschließen, hat sich die Verschachtelung von und etabliert. Netscape 4.x ignoriert das ihm unbekannte -Tag und die zugehörigen -Tags: Listing 24.5: Multimediadateien einbinden (einbinden.html)
Beachten Sie die dazugehörigen Attribute für das -Tag: ■ ■ ■
Wichtig ist das Attribut mastersound, ohne das der Navigator 4.x stumm bleibt. Das Attribut autostart legt fest, ob der Sound oder das Video sofort loslegt (true) oder nicht (false). hidden versteckt den Player (true). Standardeinstellung ist false.
Das -Tag kennt ebenfalls ein wichtiges Attribut: ■
Tabelle 24.2: Die IDs einiger wichtiger Technologien
Das Attribut classid im -Tag gibt eine Art Plugin-Identifikation an. Die folgende Tabelle zeigt eine Übersicht.
Technologie
classid (Component Identifier)
Apple QuickTime
02BF25D5-8C17-4B23-BC80-D3488ABDDC6B
Macromedia Flash Player
D27CDB6E-AE6D-11cf-96B8-444553540000
Macromedia Shockwave Player
166B1CA-3F9C-11CF-8075-444553540000
RealPlayer
CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA
Windows Media Player 6.4
22D6f312-B0F6-11D0-94AB-0080C74C7E95
Neuere Versionen des Windows Media Player
6BF52-394A-11D3-B153-00C04F79FAA6
Neben diesen Attributen können Sie für beide Tags die Höhe (height) und Breite (width) festlegen.
690
Plugins
24.2.2 Plugin-Erkennung Das navigator-Objekt besitzt die Methode plugins() zum Auslesen der Plugins im Browser. Zurückgeliefert wird ein Array mit allen Plugins. Ein einzelnes Plugin können Sie mit seinem Index oder seinem Namen ansprechen. Ein Plugin-Objekt besitzt dann vier Eigenschaften: ■ ■ ■ ■
14
description – eine Beschreibung. filename – lokaler Dateiname des Plugins. length – die Anzahl der unterstützten MIME-Typen. name – der Name des Plugins.
15
Das folgende Skript liest die Eigenschaften aus:
16
Listing 24.6: Plugins ausgeben (plugins.html) plugins()
17 18 19 20 21 22
Sie können für eine Übersicht der Plugins im Netscape Navigator auch direkt about:plugins in die Adressleiste eingeben.
23
TIPP
Neben diesen Methoden besitzt plugins() noch die Methode refresh(Aktivieren). Sie kann als Parameter true übernehmen, um die Liste mit Plugins zu aktualisieren.
navigator. plugins() a.
NS4.x
M/FF
IE4
IE5 ()a
IE5.5
IE6
IE7
Op
SF/KQ
24 Tabelle 24.3: navigator. plugins()
Nur Mac.
691
Multimedia, Java etc.
Das Problem mit der Methode plugins() ist, dass der Internet Explorer sie nicht unterstützt. Er liefert bei Aufruf dieser Methode allerdings true zurück, wenn ein soundfähiges ActiveX-Control, also der Windows Media Player oder Active Movie, vorliegt.
HALT
Der Internet Explorer ab Version 5 für Mac unterstützt navigator.plugins() vollständig. Er besitzt jedoch keine skriptfähige Erweiterung und kein ActiveX-Control, das heißt, die Steuerungsbemühungen scheitern. Wollen Sie feststellen, ob ein ActiveX-Control vorhanden ist, können Sie eine andere Methode verwenden: Listing 24.7:
Überprüfung, ob der Windows Media Player vorhanden ist (activex.html)
var control = new ActiveXObject("MediaPlayer.MediaPlayer.1"); alert(control); if(control) { alert("Media Player vorhanden"); }
Abbildung 24.3: Der Windows Media Player ist vorhanden.
MIME-Type Der MIME-Type (Multipurpose Internet Mail Extension)6 lässt sich ebenfalls aus der plugins-Kollektion gewinnen. Der Zugriff darauf ist mit einem weiteren Array möglich: navigator.plugins["Name"]["Name"]
Alternativ können Sie auch bei beiden den Index nehmen oder mischen. Eine weitere Möglichkeit besteht darin, die mimeTypes-Kollektion zu verwenden. Sie speichert in der Eigenschaft type den Namen des MIME-Types und in enabledPlugin einen Boolean, der angibt, ob dafür ein Plugin vorhanden ist. Mit diesem Wissen lässt sich die Überprüfung aus Listing 24.6 noch ein wenig ausweiten: Listing 24.8: Unterstützte MIME-Typen werden mit ausgegeben (mime.html). for (var j = 0; j < navigator.plugins[i].length; j++) { t += ""+navigator.plugins[i][j].type + ""; }
6
692
Ursprünglich eine Standardisierung für E-Mail-Anhänge, dann zur Angabe von Datei-Arten verwendet.
Plugins
Abbildung 24.4: Einige Plugins mit MIME-Typen
14 15 16 17 18 19 20 21 22 23 24
693
Multimedia, Java etc.
24.2.3 Steuerungsmöglichkeiten Die Steuerungsmöglichkeiten besitzen je nach verwendeter Technologie deutliche Unterschiede. Die folgende Tabelle zeigt die wichtigsten Steuerungsmöglichkeiten der bekanntesten Abspieltechnologien: Tabelle 24.4: Steuerungsmöglichkeiten für verschiedene Player
Netscape LiveAudio
Internet Explorer
Internet Explorer
Internet Explorer
Apple Quick- RealPlayer Time
ActiveMovie WMP 6.4
WMP 7+
Ab 4.1
Wiedergabe
play play() (Schleife)
play()
play()
play()
DoPlay()
Pause
pause()
pause()
pause()
pause()
pause()
DoPause()
Stopp
stop()
stop()
stop()
stop()
stop()
DoStop()
Abspielstatus
IsPaused() CurrentIsPlaying() State IsReady() – 0: Stopp komplett gela- 2: Pause den 3: Abspielen
PlayState 0: Stopp 2: Pause 3: Abspielen
PlayState 1: Stopp 2: Pause 3: Abspielen
volume
volume
Lautstärke GetVolume() volume
SetVol()
GetPlayerState() 0: Stopp 3: Abspielen 4: Pause
getVolume() GetVolume() SetVolume() SetVolume()
Position / - / getTime() / GetPosicurrentcurrentcurrentStart / start_time( Position / Position / Position / setTime() tion() / Ende Sekunde) / selection- selection- selection- mit play() / SetPosi-
end_time (Sekunde)
setStartStart / Start / Start / selection- selection- selection- Time() / setEndEnd End End Time()
tion()
Diese Tabelle kann nur eine grobe Übersicht über die wichtigsten Multimedia-Plugins geben. Weiterführende Informationen zu QuickTime und RealPlayer finden Sie in den nächsten beiden Abschnitten.
24.2.4 Apple QuickTime Ab Version 4.1 von QuickTime hat Apple eine JavaScript-Unterstützung eingebaut. Sie müssen dazu enablejavascript im -Tag auf true setzen. Die bekannten Steuerungsbefehle sind: ■ ■
Play() zum Abspielen Stop() zum Anhalten (nicht auf den Anfang zurück)
Leider funktioniert die JavaScript-Unterstützung in QuickTime ab 4.1 und 5 nur in Netscape 4.x. Mit QuickTime 6 gibt es Skriptfähigkeiten auch für Netscape 6, 7, Mozilla und für die ActiveX-Komponente des Internet Explorers
694
Plugins
unter Windows. Ausgesperrt wird der Internet Explorer für Macintosh, der mit ActiveX nichts anfangen kann. Mehr Informationen über die Skriptfähigkeiten von QuickTime erhalten Sie unter http://developer.apple.com/documentation/QuickTime/Conceptual/ QTScripting_JavaScript/aQTScripting_Javascro_AIntro/chapter_1_section_ 1.html.
REF
14
24.2.5 RealPlayer
15
Die Steuerung des RealPlayers ab Version G2 ist im Prinzip sehr einfach: Sie binden die Real-Datei ein und haben dann über den Namen () bzw. die ID (object) Zugriff.
16
Das folgende Beispiel verwendet die Steuerungsbefehle: Listing 24.9: RealPlayer steuern (realplayer.html)
17
RealPlayer
Beispieldateien erhalten Sie auf der Real-Homepage http://www.real.com/ oder auch bei http://www.amazon.de/. Weitere Informationen zur RealPlayer API mit vielen Steuerungsmöglichkeiten finden Sie unter http://service. real.com/help/library/guides/realonescripting/ScriptingGuide.pdf.
18 19 20 21 22 23 24
TIPP
695
Multimedia, Java etc.
24.2.6 Fazit Insgesamt bleiben die JavaScript-Steuerungsmöglichkeiten mehr oder weniger nur auf einen Player beschränkt. Auf unterschiedliche Player und Plattformen zu reagieren erfordert eigentlich je zwei selbstständige Skripte. Die Ähnlichkeiten zwischen LiveAudio und dem Windows Media Player zu nutzen macht heute kaum noch Sinn, da LiveAudio in den aktuellen NetscapeBrowsern nicht mehr integriert ist. Als Standards in Bezug auf Skriptzugriff kristallisieren sich immer mehr der Windows Media Player, der RealPlayer und mit Einschränkungen der QuickTime heraus.
TIPP
Wer selbst ein Netscape-kompatibles Plugin schreibt, findet interessante Informationen zur neuen Mozilla API für Scripting-Schnittstellen unter http://www.mozilla.org/projects/plugins/scripting-plugins.html.
24.3 Flash Flash ist ein Quasistandard für Vektoranimationen im Internet. Die Entwicklungsumgebung Flash und das Format SWF (Small Web Format)7 stammen von der Firma Adobe, die die ursprüngliche Firma Macromedia übernommen hat.8 Flash besitzt eine eigene Skriptsprache namens ActionScript, die seit Flash 5 wie JavaScript auf ECMAScript basiert. Das heißt, für alle, die JavaScript können, ist ActionScript sehr leicht zu erlernen (und umgekehrt). Die Ähnlichkeit der beiden Sprachen ist allerdings nicht das einzige Bemerkenswerte. Macromedia hat in den Flash Player auch Austauschmöglichkeiten zwischen JavaScript und Flash eingebaut. Beide Kommunikationsrichtungen sind möglich, wie die nächsten beiden Abschnitte zeigen werden. Die Einbindung eines Flash-Films erfolgt mit und . Flash erledigt dies beim Veröffentlichen des Films in der Entwicklungsumgebung auch selbst. Hier der produzierte Code:
Die classid stellt Flash dankenswerterweise selbst ein. Ansonsten können Sie die Einstellungen natürlich auch anpassen. INFO
14
24.3.1 JavaScript zu Flash Der Zugriff auf den Flash-Film erfolgt ähnlich wie bei Musikstücken über den Namen () bzw. die ID (). Name und ID sollten natürlich gleich sein. Den swf-Film wuerfel.swf und die Flash-Datei finden Sie beide im Download-Archiv im Ordner code\kap24. Der Film zeigt einen fliegenden Würfel und ist wirklich einfach zu realisieren. Die zugehörige HTML-Seite, die Flash generiert hat, heißt wuerfel.html.
15 16 WWW
17
Zuerst müssen Sie in der Seite wuerfel.html das Attribut swLiveConnect="true" setzen, damit auch Netscape 4.x auf den Flash-Film zugreifen kann. Fügen Sie in die HTML-Seite drei Schaltflächen für Start, Pause und Stopp hinzu. Die dazugehörigen Funktionen sehen so aus:
18 19
Listing 24.10: Flash-Film-Steuerung aus JavaScript (wuerfel_modifiziert.html) function play() { document.wuerfel.Play(); } function pause() { document.wuerfel.StopPlay(); } function stop() { document.wuerfel.Rewind(); }
Der Internet Explorer verwendet standardmäßig den Zugriff mit window.Filmname, kennt allerdings document.Filmname ebenfalls. Wollen Sie auf Nummer sicher gehen, können Sie eine Fallunterscheidung einfügen. Eine solche Datei finden Sie im Download-Archiv unter dem Namen wuerfel_ sicher.html im Verzeichnis code\kap24.
20 21 22 23 WWW
24
697
Multimedia, Java etc.
Abbildung 24.5: Den Flash-Film anhalten
Methoden Die im letzten Beispiel eingesetzten Methoden waren natürlich nicht die einzigen für den Zugriff auf Flash. Die folgende Tabelle gibt eine Übersicht: Tabelle 24.5: Methoden zur Flash-Steuerung aus JavaScript
Methode
Beschreibung
GetVariable(Name)
Liefert den Wert einer ActionScript-Variablen Name.
GotoFrame(Nummer)
Springt zu einer Frame-Nummer im Film. Der Film läuft nicht weiter.
IsPlaying()
Prüft, ob der Film abgespielt (true) oder angehalten wird (false).
LoadMovie(Position, Datei) Lädt einen Film auf das angegebene Level. Entspricht weitgehend der LoadMovie-Methode in ActionScript.
Pan(x, y, Modus)
698
Schwenkt einen gezoomten Bereich. x und y stehen für die x- und yKoordinate, wenn der Modus den Wert 0 hat. Ansonsten stehen sie für Prozentwerte.
Flash
Tabelle 24.5: Methoden zur Flash-Steuerung aus JavaScript (Forts.)
Methode
Beschreibung
PercentLoaded()
Liefert, wie viel Prozent von 0 bis 100 eines Films geladen wurden.
Play()
Spielt den Film ab.
Rewind()
Setzt den Film auf den ersten Frame und stoppt ihn.
SetVariable(Name, Wert)
Setzt eine ActionScript-Variable Name auf den Wert.
14
SetZoomRect(l, o, r, u)
Zoomt einen rechteckigen Bereich aus dem Film. Die Koordinaten für links (l), oben (o), rechts (r) und unten (u) werden in twips angegeben. Auf ein Inch kommen 1140 twips.
15
StopPlay()
Hält den Film an.
TotalFrames()
Anzahl der Frames im Film.
Zoom(Faktor)
Verkleinert oder vergrößert den Film relativ zu einer Basis von 100.
16 17
Alle hier vorgestellten Methoden funktionieren seit dem Flash Player 4, viele sogar noch länger. Proprietäre Erweiterungen des ActiveX-Controls und undokumentierte Methoden berücksichtigt die Tabelle nicht und Sie sollten im Praxiseinsatz darauf verzichten. Neben den hier genannten allgemeinen Methoden gibt es noch Tell-Target-Methoden für die Arbeit mit einzelnen Movieclips (vormals Filmsequenzen).
18
INFO
19
24.3.2 Flash zu JavaScript
20
In ActionScript, der Skriptsprache von Flash, gibt es zwei Wege, mit JavaScript zu kommunizieren:
21
■
■
Die Methode fscommand(Befehl, Parameter), mit der Sie ein Kommando übertragen und als optionale Angabe eigene Parameter übergeben können. Die Kommandos waren ursprünglich dafür vorgesehen, dem Flash Player Befehle zukommen zu lassen, was auch heute noch möglich ist. Alternativ erlauben sie aber auch die Kommunikation mit JavaScript. Die zweite Alternative ist getURL(), mit der Sie einen Link angeben können. Verwenden Sie dort einen JavaScript-Link, haben Sie schon einen Aufruf einer JavaScript-Funktion:
22 23 24
fscommand() Der Einsatz von fscommand() ist ein wenig aufwändig, da Sie wie erwähnt nicht direkt mit JavaScript-Befehlen arbeiten können. Ein Beispiel zeigt dies: Ausgangspunkt ist eine Flash-Datei mit einem Eingabefeld und einer Schaltfläche. Klickt der Nutzer auf die Schaltfläche, wird fscommand() ausgeführt: on (release) { fscommand("Ausgabe", text); }
699
Multimedia, Java etc.
Der Code gibt als Befehl »Ausgabe« und übergibt als Parameter den Wert des Textfelds, der vorher in Form einer Variablen im Eigenschafteninspektor angegeben wurde. Sie finden die Datei im Download-Archiv unter dem Namen fscommand.fla im Ordner code\kap24. WWW
Wenn die Flash-Datei fertig ist, sind folgende Schritte notwendig: 1.
Abbildung 24.6: Wählen Sie FLASH MIT FSCOMMAND
700
Sie müssen die Flash-Datei veröffentlichen. Hierzu gibt es in den Einstellungen für das Veröffentlichen (DATEI/EINSTELLUNGEN FÜR VERÖFFENTLICHUNGEN) im Register HTML die Option FLASH MIT FSCOMMAND. Sie erzeugt automatisch eine Seite, die fscommand()-Anweisungen aufnimmt.
Flash
2.
Die von Flash produzierte HTML-Seite sollten Sie im Texteditor näher betrachten. Sie enthält eine Browserunterscheidung. Der Netscape Navigator mit LiveConnect9 wird per JavaScript angesprochen. Da die ActiveX-Komponente des Flash Players nicht JavaScript-fähig ist, verwendet Flash hier ein per JavaScript erzeugtes VBScript.10 In der Seite ist der wichtigste Teil die JavaScript-Funktion function fscommand_DoFSCommand(command, args). Ihr Name wird aus dem Namen des Flash-Films, hier also fscommand und _DoFSCommand, zusammengesetzt. Der Befehl und die Parameter erscheinen beide als Parameter.
14 15
Da der Dateiname des Flash-Films Bestandteil einer JavaScript-Funktionsbezeichnung ist, sollten Sie darin keine Sonderzeichen verwenden!
16
HALT
3. Fügen Sie nun folgenden Code in die Funktion ein, um den Text aus dem Formularfeld auszugeben:
17
Listing 24.11: if (cofscommand() in JavaScript (fscommand.html) mmand == "Ausgabe" && args != "") alert(args);
18
Fertig ist das Beispiel. Der zusätzliche Schritt, bei dem der eigentliche Code in JavaScript steht, ist leider notwendig. Alternativ können Sie auch eine komplette JavaScript-Anweisung in das Kommando schreiben und diese dann mit
19
eval(command);
20
ausführen. Abbildung 24.7: Der Text aus dem Flash-Eingabefeld landet in einer JavaScript-Warnmeldung.
21 22 23 24
9
Dies funktioniert in einigen Versionen des Netscape Navigators nicht, da die LiveConnect-Unterstützung dort fehlt oder fehlerhaft ist. Bekannt sind Probleme bei Navigator 6.0 bis 6.1 und 4.76. 10 Dies funktioniert nicht im Internet Explorer für Mac, da dort kein ActiveX vorhanden ist.
701
Multimedia, Java etc.
getURL() Der Einsatz von getURL() ist sehr einfach. Sie übergeben den JavaScriptBefehl als JavaScript-Link. getURL("javascript:alert(\'Hallo von Flash!\')\;");
Sie können damit auf alle Elemente der HTML-Seite zugreifen und in Flash den Link auch bei verschiedensten Ereignissen, beispielsweise beim Klicken auf eine Schaltfläche, ausführen.
INFO
Neben den Standardkommunikationsmöglichkeiten arbeitet Adobe noch an einigen anderen Alternativen. Unter anderem soll die Flex-Ajax-Bridge eine Verbindung zwischen serverseitig generiertem SWF und JavaScript erlauben (http://labs.adobe.com/wiki/index.php/Flex-Ajax_Bridge).
24.3.3 Kommunikationssorgen Wie sinnvoll die Kommunikation über fscommand() ist und wann Sie besser auf getURL() zurückgreifen, hängt vom Einzelfall ab. Die bei fscommand() ausgeschlossenen Browser sollten Sie unter Umständen durch Überprüfung vorher herausfiltern. Vor allem beim Internet Explorer für Mac ist das sinnvoll, da diese Nutzer einen anderen Browser verwenden müssen. Ebenso achten Sie bitte auf die nicht berücksichtigten Browser wie Opera, Konqueror und Safari. Neben der Überprüfung des Browsers macht es durchaus Sinn, auch das Flash-Plugin zu testen. Die Überprüfung für den Navigator und Mozilla erfolgt über: navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin
Die Versionsnummer lässt sich in der description-Eigenschaft auslesen: navigator.mimeTypes["application/x-shockwave-flash"].description
TIPP
702
Die Erkennung im Internet Explorer erfolgt über VBScript und ist um einiges komplizierter. Flash liefert entsprechende Vorlagen im Register HTML der Einstellungen zum Veröffentlichen (DATEI/EINSTELLUNGEN FÜR VERÖFFENTLICHUNGEN).
Teil 4 Ajax-Frameworks Ajax-Frameworks helfen dabei, Ajax-Anwendungen zu entwickeln. Im Moment gibt es hunderte von Ajax-Rahmenwerken mit ganz unterschiedlichen Ausprägungen. Dieses Buch möchte und kann keinen kompletten Marktüberblick geben. Stattdessen werden drei Frameworks herausgegriffen, die stellvertretend für verschiedene Ansätze und für unterschiedliche Komplexitätsstufen stehen. Im vierten Kapitel dieses Teils (Kapitel 28 »Mehr Frameworks«) erhalten Sie dann noch einen kurzen Überblick über andere Rahmenwerke. Die Auswahl der drei Frameworks folgt dabei keinen wissenschaftlichen Kriterien. Auch erhebt sie nicht den Anspruch, die besten Frameworks zu wählen. Entscheidend war nur, dass die Frameworks sich alle schon in der Praxis bewährt haben.
705
Xajax
25
725
Prototype und scriptaculous
26
767
Microsoft ASP.NET AJAX
27
799
Mehr Frameworks
28
Inhalt
25 Xajax 25 26
Xajax gehört in die Gruppe der Frameworks, die sich auf eine bestimmte Technologie konzentrieren. In diesem Fall ist das PHP, der Marktführer unter den serverseitigen Technologien. Das Xajax-Projekt finden Sie unter http://www.xajaxproject.org/ und unter http://xajax.sourceforge.net/.
28 INFO
29
Xajax ist typisch für Ajax-Bibliotheken mit Fokus auf einer serverseitigen Technologie: Der Ajax-Aufruf wird stark gekapselt, sodass der Entwickler in »seiner« Technologie schreiben kann und sich relativ wenig mit JavaScript beschäftigen muss. Ohne geht es allerdings auch in diesem Fall nicht – nicht umsonst legt dieses Buch so viel Wert auf JavaScript.
30
Abbildung 25.1: Die Xajax-Website
705
ndex
Andere auf PHP spezialisierte Frameworks sind beispielsweise Sajax (http://www.modernmethod.com/sajax/) und das PEAR-Paket HTML_AJAX (http://pear.php.net/package/HTML_AJAX). Sajax besitzt mittlerweile allerdings auch ein Perl- und Python-Backend.
27
Xajax
Eine weitere Besonderheit ist bei Xajax die enge Anbindung mit dem populären Open-Source-Content-Management-System Typo3 – Xajax steht dort als Erweiterung zur Verfügung. Mehr dazu im Abschnitt 25.5 »Besonderheiten«. Xajax beschränkt sich hauptsächlich auf die Ajax-Verbindung. JavaScriptEffekte wie Drag&Drop und Ähnliches sind in Xajax nicht integriert. Hier unterscheidet sich die Bibliothek deutlich von JavaScript-basierten Lösungen wie Prototype und von umfassenden Lösungen wie Microsoft ASP.NET Ajax (vormals Atlas).
25.1
Einrichtung
Laden Sie als Erstes Xajax von Sourceforge herunter. Ausgeliefert wird das Framework in einem ZIP-Archiv. Herzstück ist die Datei xajax.inc.php. Sie enthält die entsprechenden Klassen für Xajax und verweist auf die anderen Bibliotheken im ZIP-Archiv (xajaxResponse.inc.php und xajaxCompress.php). Diese Datei muss nun nur noch in eine PHP-Datei eingefügt werden, die die Xajax-Funktionalität verwendet: require_once('xajax.inc.php');
Die eigentliche Ajax-Verbindung wird dann in diese PHP-Datei geschrieben (siehe nächster Abschnitt).
INFO
Für Xajax benötigen Sie einen Webserver mit PHP ab Version 4.3 bzw. Version 5. Für lokale Tests bietet sich ein Installer wie XAMPP (http://www. apachefriends.org/) an, der den Apache als Webserver, PHP als serverseitige Technologie und MySQL als Datenbank gleich mit installiert. XAMPP gibt es für Windows, Linux, Mac OS X und Solaris.
25.2 Ajax-Verbindung Die einfachste Architektur für einen Ajax-Aufruf basiert bei Xajax aus nur einer PHP-Datei: In diese Datei schreiben Sie sowohl die serverseitige Methode, die Daten an JavaScript übergibt, als auch den JavaScript-Aufruf. 1.
Zuerst folgt wie schon erwähnt das Einbinden der Xajax-Bibliothek: require_once 'xajax.inc.php';
2.
Anschließend erstellen Sie ein neues Objekt: $xajax = new xajax();
3. Nun müssen Sie eine PHP-Funktion für Xajax bestimmen. Sie übernimmt dann die Rücklieferung der Daten an den Ajax-Aufruf: $xajax->registerFunction('datum');
4. Die Funktion datum() selbst enthält ein neues xajaxResponse-Objekt. Die dahintersteckende Klasse ist für die Gestaltung der Antworten zuständig. Xajax verwendet an dieser Stelle noch einige Methoden wie
706
Ajax-Verbindung
addAssign(), die es erlauben, Eigenschaften von HTML-Elementen zu setzen, ohne JavaScript-Code selbst zu schreiben. In diesem Fall wird mit addAssign() und der Eigenschaft innerHTML ein -Element mit dem aktuellen Datum befüllt: function datum() { $antwort = new xajaxResponse(); $antwort->addAssign('ausgabe', 'innerHTML', date('H:i:s d.m.Y')); return $antwort; }
25
5. Nun müssen Sie noch Xajax das Startsignal geben:
26
$xajax->processRequests();
6. Die einzige JavaScript-Arbeit fällt beim Aufruf der Ajax-Methode an. Der Funktionsname ist der Name der PHP-Funktion, ergänzt um ein xajax_ am Anfang. In unserem Beispiel rufen Sie die Funktion beim Laden der Seite auf:
27
28
window.onload = function() { xajax_datum(); }
29
Im Archiv von Xajax befinden sich die JavaScript-Dateien im Unterverzeichnis xajax_js. Wenn Sie ein anderes Verzeichnis auf Ihrem Server angeben möchten, übergeben Sie es der Methode printJavascript() als Parameter. Hier der vollständige Code: Listing 25.1:
30
Ein grundlegender Xajax-Aufruf (xajax_datum.php)
Datenaustausch mit xajax
707
Xajax
window.onload = function() { xajax_datum(); }
Abbildung 25.2: Das aktuelle Datum dank Xajax
25.3 Nützliche Methoden Das Verhalten von Xajax steuern Sie über eine Vielzahl von Methoden. Sie dienen zur Konfiguration und zur Ausgabe. Außerdem gibt es noch verschiedene Arten, Funktionen zu registrieren, und einige JavaScript-Methoden.
25.3.1 Konfiguration Die Xajax-Bibliothek bietet einige Konfigurationsoptionen, mit denen die Datenübertragung gesteuert und Fehlermeldungen ein- und ausgeschaltet werden können. Die folgende Auflistung gibt einen kleinen Überblick: ■
setCharEncoding(Zeichensatz) setzt den Zeichensatz für den Datenaustausch. Mögliche Werte sind hier ISO-8859-1, der Latin-Zeichensatz mit deutschen Umlauten, und UTF-8, der Unicode-basierte Zeichensatz. $xajax->setCharEncoding('iso-8859-1');
■
Die Methode debugOff() deaktiviert die Debugging-Ausgaben der XajaxBibliothek. Das Gegenstück ist debugOn(). Die Debug-Meldungen werden in einem eigenen Fenster angezeigt. $xajax->debugOn();
■
Meldungen über den Status der Requests liefert die Methode statusMessagesOn() und statusMessagesOff(). $xajax->statusMessagesOn();
708
Nützliche Methoden
Abbildung 25.3: Xajax meldet sich bei Problemen.
25 26
27
28
waitCursorOn() zeigt einen Wartecursor beim Laden. Dies ist der Standard. waitCursorOff() schaltet den Cursor aus.
■
Abbildung 25.4: Der Wartecursor
30
Auch das Error-Handling lässt sich an- und abschalten. Zuständig sind die Methoden errorHandlerOn() und errorHandlerOff(). Die entsprechenden Fehlermeldungen lassen sich auch mit setLogFile(Datei) in eine Logdatei auslagern.
■
Das folgende Beispiel zeigt einige Konfigurationseinstellungen. Um eine längere Wartezeit beim Ajax-Aufruf zu simulieren, kommt die PHP-Funktion sleep() zum Einsatz. Listing 25.2:
29
Xajax-Konfiguration (xajax_konfiguration.php)
709
Xajax
Datenaustausch mit xajax Hier klicken
INFO
Mehr Konfigurationsmöglichkeiten entnehmen Sie der offiziellen XajaxDokumentation im Xajax-Wiki (http://wiki.xajaxproject.org/). Dort sind auch die einzelnen Include-Dateien und die dort enthaltenen Methoden dokumentiert (http://wiki.xajaxproject.org/Documentation:xajax.inc.php).
25.3.2 Parameter übernehmen Bisher sind die Xajax-Aufrufe erfolgt, ohne Parameter aus der HTML-Seite zu übernehmen. Dies soll sich nun ändern. Die einfachste Art, einen Parameter zu übergeben, besteht darin, ihn in den JavaScript-Funktionsaufruf zu integrieren. In der PHP-Funktion muss der Parameter dann implementiert werden und steht dann für die Weiterverarbeitung zur Verfügung. Hier ein Beispiel, das die Summe aus zwei Zahlen bildet: Listing 25.3:
Xajax mit Parametern (xajax_parameter.php)
710
Nützliche Methoden
Datenaustausch mit xajax
25
a b
26
27
28 Abbildung 25.5: Die Summe wird berechnet.
29
30
Alle Daten, die Sie per Ajax-Aufruf erhalten, können potenziell manipuliert sein. Das heißt, Sie müssen diese Daten unbedingt filtern. Wenn Sie Daten z.B. ungefiltert in eine Datenbank übernehmen, riskieren Sie das Einschleusen von gefährlichem SQL-Code, auch weithin bekannt als SQL-Injection.
TIPP
Nun ist es natürlich umständlich, alle Daten eines Formulars einzeln als Parameter zu übergeben. Deswegen besitzt Xajax eine JavaScript-Hilfsmethode namens xajax.getFormValues(ID), die alle Datenfelder eines Formulars, das über seine ID angegeben wird, als Parameter per assoziatives Array übergibt.
711
Xajax
Hier das oben gezeigte Summenbeispiel mit dieser Methode: Listing 25.4: Alle Formulardaten übermitteln (xajax_parameter_form.php) Datenaustausch mit xajax a b
25.3.3 Request steuern Bisher stecken die Ajax-Seite und das aufgerufene Skript in derselben PHPDatei. Dies lässt sich relativ leicht ändern. Mit der Methode setRequestURI(URL) setzen Sie die URL des aufgerufenen Skripts und können so die beiden Teile in zwei Dateien aufspalten. Die HTML-Datei enthält den Ajax-Aufruf und gibt die URL der anderen Datei an. Wichtig ist, dass die
712
Nützliche Methoden
Funktion hier mit registerFunction() registriert ist, da sie sonst im JavaScript-Skript nicht zur Verfügung steht: Listing 25.5:
Der Xajax-Request (xajax_request.php)
25 26
Datenaustausch mit xajax window.onload = function() { xajax_datum(); }
27
28
29
30
Im serverseitigen Skript müssen Sie nur dieselbe Funktion registrieren. Hier ist außerdem die Funktion serverseitig implementiert: Listing 25.6: Das Skript zum Beantworten des Requests (xajax_request2.php)
In dem Xajax-Request-Prozess gibt es aber noch mehr Steuerungsmöglichkeiten. Mit registerFunction() melden Sie Funktionen bei Xajax an und stellen sie damit zum Datenaustausch zur Verfügung. Der erste Parameter 713
Xajax
von registerFunction() ist die Funktion, die registriert wird. Der optionale zweite Parameter ist die HTTP-Methode, mit der Daten ausgetauscht werden. Hier stehen GET oder POST zur Wahl, die allerdings mit den XajaxKonstanten XAJAX_GET und XAJAX_POST angesprochen werden. Der Standardwert ist POST. Listing 25.7:
Xajax per GET (Ausschnitt aus xajax_get.php)
registerFunction('datum', XAJAX_GET);
Abbildung 25.6: Der Aufruf erfolgt per GET.
Die Versandmethode können Sie mit der Methode getRequestMode() abfragen. TIPP
registerFunction() bietet allerdings noch ein wenig mehr. Sie können nicht nur Funktionen registrieren, sondern auch Methoden von statischen Klassen und Methoden von Objekten. Der erste Parameter wird dabei zu einem Array, das aus drei Elementen besteht: ■
714
Dem Namen der JavaScript-Funktion. Er ist hier im Gegensatz zum einfachen Ansatz mit nur einem Namen unabhängig vom Namen der serverseitigen Methode!
Nützliche Methoden
Dem Namen der Klasse als String bei einem statischen Zugriff. Bei einem Objekt wird das Objekt direkt übergeben. In PHP 4 muss es mit einem Et-Zeichen (&) als Referenz übergeben werden. Und zu guter Letzt dem Namen der aufgerufenen Methode.
■
■
Hier ein Beispiel, das die Methode getDatum() statisch aufruft:
25
Listing 25.8: Xajax-Aufruf einer Methode (Ausschnitt aus xajax_register.php)
Eine Alternative zu registerFunction() ist registerExternalFunction(Funktion, Datei, Methode). Sie besitzt dieselben Fertigkeiten wie registerFunction() inklusive der Möglichkeit, statische Methoden oder Methoden von Objekten aufzurufen. Als Bonus kann sie die Funktion aber aus einer externen Datei aufrufen. Das sieht dann so aus:
30
Listing 25.9: Externe Funktion aufrufen (xajax_register_external.php) $xajax->registerExternalFunction(array('datum', 'xajaxMethoden', 'getDatum'), 'external.inc.php', XAJAX_POST);
Wenn Sie eine Funktion vor dem Aufruf normaler Funktionen durchführen möchten, verwenden Sie registerPreFunction(Funktion). Damit kann beispielsweise eine Funktion zur Sicherheitsüberprüfung aufgerufen werden. Scheitert die Authentifizierung, geben Sie ein Array zurück, dessen erster Wert false und dessen zweiter Wert die Antwort der Funktion ist. Dann wird die nachfolgende Funktion nicht mehr aufgerufen. Die letzte interessante Funktion in der Reihe der Aufruf-Abfänger ist registerCatchAllFunction(Funktion). Sie legt eine Funktion (oder Methode) fest, die aufgerufen wird, wenn ein JavaScript-Xajax-Aufruf ins Leere geht, also keine Funktion erreicht. Die JavaScript-Funktionen in Xajax werden automatisch mit dem Präfix xajax_ versehen, damit gleichnamige JavaScript-Funktionen nicht überschrieben werden. Dieses Präfix können Sie mit der Methode setWrapperPrefix(Prefix) ändern. Dementsprechend müssen Sie natürlich auch den Aufruf der JavaScript-Funktion im JavaScript-Code oder Ereignis anpassen.
715
Xajax
INFO
Alternativ zur Registrierung der einzelnen Angaben mit einzelnen Methoden können Sie hier auch den Konstruktor verwenden. Geben Sie dazu die Einstellungen einfach beim Erstellen des xajax-Objekts an: xajax(URL, Prefix, Zeichensatz, Debugging).
25.3.4 Ausgabe Die Ausgabe wird in der serverseitigen Funktion für die Ajax-Antwort per Methode gesteuert. Diese Methoden übersetzt Xajax in eine eigene XMLSprache, die dann wiederum von der clientseitigen JavaScript-Bibliothek verstanden und umgesetzt wird.
INFO
Dieses Verfahren ähnelt ein wenig dem Einsatz von XMLScript in Microsoft ASP.NET Ajax. Dort wird das XML allerdings als Ersatz für clientseitigen Code direkt im Client ausgegeben, statt erst über XMLHttpRequest übertragen zu werden. Beide Ansätze haben ihre Vor- und Nachteile. Bei Xajax muss man sich nicht mit JavaScript und einer clientseitigen XML-Sprache auseinandersetzen. Dafür steigt aber die Datenmenge der einzelnen XMLHttpRequest-Aufrufe, da die Kommandoinformationen für die clientseitige XajaxBibliothek mitgeliefert werden müssen. Die Xajax-Datei xajaxResponse.inc.php ist für die zur Verfügung stehenden Methoden zuständig. Basis ist das xajaxResponse-Objekt: $antwort = new xajaxResponse();
Nun kann mit der schon bekannten Funktion addAssign(Element, Attribut, Wert) ein beliebiges HTML-Attribut oder eine Eigenschaft gesetzt werden. In den bisherigen Beispielen war das meist innerHTML, um textliche Inhalte einzufügen. addAssign() kann aber auch für andere HTML-Attribute wie CSS-Stile verwendet werden: $antwort->addAssign('ausgabe', 'style.color', 'red');
Neben addAssign() gibt es noch einige andere Methoden, die die Werte von Attributen auf andere Arten setzen: ■ ■ ■ ■ ■
716
addAppend(ID, Attribut, Wert) fügt den Wert vor die übrigen Werte des Attributs oder der Eigenschaft ein. addPrepend(ID, Attribut, Wert) fügt den Wert nach den übrigen Werten des Attributs oder der Eigenschaft ein. addReplace(ID, Attribut, Suchbegriff, Wert) ersetzt einen Text aus einem Attribut bzw. einer Eigenschaft mit dem angegebenen Wert. addClear(ID, Attribut) lehrt das Attribut bzw. die Eigenschaft eines Elements. addCreate(Parent, Tag, ID) erstellt ein Element unter dem angegebenen Element.
Nützliche Methoden
addRemove(ID) entfernt das angegebene Element. addAlert(Meldung) fügt eine JavaScript-Warnmeldung hinzu, die direkt angezeigt wird. addRedirect(URL, Verzögerung) übermittelt eine (natürlich JavaScriptbasierte) Weiterleitung. addScript(Skript) fügt JavaScript-Code zur Ausgabe hinzu. Damit können Sie eigene Inhalte einfügen. addScriptCall(Funktion, Para1, Para2) fügt einen Funktionsaufruf hinzu. Die Parameter, die an die Funktion übergeben werden, folgen nach dem Funktionsnamen.
■ ■ ■ ■ ■
25 26
Das folgende Beispiel ersetzt einen Platzhalter in dem -Block und ändert Farbe und Fettdruck für das -Element:
27
Listing 25.10: Einige Ausgabemethoden von Xajax (xajax_ausgabe.php) Datenaustausch mit xajax window.onload = function() { xajax_datum(); }
717
Xajax
Datum: Platzhalter
Abbildung 25.7: Die Ausgabe wird an der richtigen Stelle eingesetzt und formatiert.
INFO
Weitere Methoden finden Sie in der Dokumentation für die Include-Datei unter http://wiki.xajaxproject.org/Documentation:xajaxResponse.inc.php. Neben den Methoden für die Ausgabe gibt es noch ein paar Möglichkeiten des xajaxResponse-Objekts, um die Rückgabe zu steuern. Mit getXML() erhalten Sie das XML, das von der jeweiligen Antwort übergeben wird. Ab Version 0.2 von Xajax ist das nicht mehr notwendig. Allerdings können Sie damit und mit der Methode loadXML(XML) auch zwei Xajax-Antworten zusammenfügen. Listing 25.11: XML auslesen und laden (xajax_loadxml.php) $antwort = new xajaxResponse(); $antwort->addAssign('ausgabe','innerHTML', date('H:i:s d.m.Y')); $antwort->addAssign('ausgabe', 'style.color', 'red'); $antwort2 = new xajaxResponse(); $antwort2->addAssign('ausgabe', 'style.fontWeight', 'bold'); $antwort2->loadXML($antwort->getXML()); return $antwort2;
25.3.5 JavaScript-Methoden Xajax bietet neben den PHP-Methoden zur Steuerung der Kommunikation auch noch einige JavaScript-basierte Hilfsfunktionen. Dazu gehört xajax. getFormValues(). Und auch für document.getElementById() gibt es eine Alternative. Sie heißt xajax.$(ID). Hier ein Beispiel, das zwei Parameter aus dem schon von Abschnitt 25.3.2 bekannten Formular übergibt:
718
Produktsuche – ein Beispiel
Listing 25.12: $-Operator (xajax_getelementbyid.php)
Eine Dokumentation der verfügbaren Methoden finden Sie unter http:// wiki.xajaxproject.org/Documentation:xajax_uncompressed.js.
25
INFO
25.4 Produktsuche – ein Beispiel 26
Die Grundzüge von Xajax sind wie die von Ajax an sich angenehm einfach. Erst wenn man mehrere Funktionen zusammensetzt, wird daraus ein praktisches Beispiel. Ein solches soll in diesem Abschnitt gezeigt werden. Grundlage ist eine Produktsuche. Eine Auswahlliste zeigt die Produktgattungen. Wählt der Nutzer eine Kategorie aus, erhält er eine Auswahlliste mit allen Produkten. Diese Liste erscheint dabei ohne Neuladen der Seite (also per Ajax).
27
28
Grundlage des Beispiels ist eine externe Datenquelle mit den Produkten. Das kann natürlich eine Datei oder eine Datenbank sein. Allerdings soll es hier nicht um die Datei- und Datenbankfunktionen von PHP gehen, deswegen begnügen wir uns mit einem einfachen PHP-Array, das als externe Datei geladen wird:
29
30
require 'produkte.inc.php';
Das Array besteht aus zwei Gattungen mit jeweils einigen Pflanzen mit ein wenig unsachlichen Beschreibungen: Listing 25.13: Die Datenquelle (produkte.inc.php)
Nun aber zu den Besonderheiten des Beispiels. Das Beispiel besteht aus zwei bei Xajax registrierten Funktionen produkte() und produktdetails(). Die Funktion produkte() wird aus der schon im HTML-Code angezeigten Auswahlliste produktgruppe aufgerufen:
719
Xajax
Bitte wählen
Die Funktion Produkte () bindet zuerst die Datenquelle, hier also die externe PHP-Datei, ein. Die Einbindung muss explizit in der Funktion erfolgen, da beim Xajax-Request nur diese Funktion ausgeführt wird: function produkte($daten) { require 'produkte.inc.php';
Anschließend wird auf die Auswahlliste zugegriffen. Xajax übergibt den gewählten Wert in dem Array $daten. Das Skript prüft, ob der Wert gesetzt wurde und ob die Produktgruppe existiert. Ist das der Fall, werden die Produkte ausgelesen und in einer zweiten Auswahlliste ausgegeben: $produktgruppe = isset($daten['produktgruppe']) ? $daten['produktgruppe'] : 'Kakteen'; $antwort = new xajaxResponse(); if (isset($produkte[$produktgruppe])) { $ausgabe = ''; $ausgabe .= 'Bitte wählen'; foreach ($produkte[$produktgruppe] as $schluessel => $wert) { $ausgabe .= '' . $schluessel . ''; } $ausgabe .= ''; $antwort->addAssign('ausgabe_produkte', 'innerHTML', $ausgabe); } else { $antwort->addAssign('ausgabe_produkte', 'innerHTML', 'Keine passenden Produkte gefunden'); } return $antwort; }
Die Funktion produktdetails() verfährt ähnlich, nur dass die Informationen zu dem in der zweiten Auswahlliste gegebenen Produkt statt in einer Liste direkt in einem -Block ausgegeben werden. Hier der vollständige Code: Listing 25.14: Eine praktische Anwendung mit Xajax (xajax_produktsuche.php) Datenaustausch mit xajax #ausgabe_produkte { margin-top: 10px;
721
Xajax
margin-bottom: 10px; } Bitte wählen
Abbildung 25.8: Der Nutzer wählt ...
Abbildung 25.9: … und erhält die Antworten ohne Neuladen.
722
Besonderheiten
25.5 Besonderheiten Xajax besitzt – wie viele Ajax- und JavaScript-Frameworks – auch noch einige Besonderheiten. Eine ist die Integration in das bekannte und beliebte Open-Source-CMS Typo3. Hierfür steht die Typo3-Extension xajax im offiziellen Typo3 Extension Repository unter http://www.typo3.org/ zur Verfügung. In der Praxis ändert sich durch den Einsatz als Typo3-Extension wenig. Einzig beim Einbinden müssen Sie die Typo3-Konventionen beachten. Hier hilft das Tutorial weiter, das Sie unter dem Schlüssel xajax_tutor im Typo3 Extension Repository finden.
25 26
Als weitere Funktionalität wird die Integration mit Smarty angepriesen. Sie bezieht sich vor allem auf die Integration des Xajax-JavaScript-Codes. Statt ihn mit printJavascript() direkt auszugeben, können Sie ihn mit getJavascript() an einen Smarty-Platzhalter übergeben:
27
$smarty->assign('xajax_javascript', $xajax->getJavascript());
28
Der Platzhalter steht dann in folgender Form zur Verfügung: {$xajax_javascript}
29
30
723
Inhalt
26 Prototype und scriptaculous 25
26
Die JavaScript- und Ajax-Bibliothek Prototype stammt aus dem Jahr 2005 und wurde von Sam Stephenson entwickelt. Die offizielle Website ist http://www.prototypejs.org/. Besonders bekannt geworden ist Prototype allerdings durch die Integration in das erfolgreiche serverseitige Framework Ruby on Rails.
27
Abbildung 26.1: Prototype ist hauptsächlich der Arbeit eines Entwicklers zu verdanken.
28
29
30
725
ndex
In Ruby on Rails bildet Prototype ein kongeniales Duo mit scriptaculous (http://script.aculo.us/). scriptaculous basiert vollständig auf Prototype, verwendet also die Prototype-Funktionen und liefert selbst eine Menge an DHTML-Effekten von der Animation bis zum Drag&Drop. Trotz der populären Integration in Ruby on Rails sind beide Bibliotheken auch unabhängig von diesem Framework verwendbar. Sie sind dann ganz normale JavaScriptBibliotheken, die man einbindet. scriptaculous selbst gibt es allerdings nicht ohne Prototype, da weite Teile des Programmiermodells darauf basieren.
Prototype und scriptaculous
Abbildung 26.2: Die scriptaculousHomepage
Die Ajax-Funktionalität steckt komplett in Prototype. scriptaculous liefert allerdings einige Ajax-Controls, mit denen die Prototype-Funktionen erweitert werden können. Die folgenden Abschnitte behandeln jeweils zuerst Prototype und zeigen dann, wie die Prototype-Funktionalität mit scriptaculous erweitert werden kann.
INFO
Die Dokumentation von scriptaculous (http://wiki.script.aculo.us/ scriptaculous/) ist ein wenig besser als die von Prototype (http:// www.prototypejs.org/api). Beide geben aber eine grundlegende Übersicht über die Möglichkeiten. Grundlegendes zum Einsatz und lauffähige Beispiele finden Sie in diesem Kapitel.
26.1 Einrichtung Die Prototype-Bibliothek besteht nur aus einer einzigen Datei. Sie enthält alle JavaScript-Funktionalitäten, die aus Ruby on Rails herausgelöst wurden. Um sie einzubinden, fügen Sie einfach die folgende Zeile in Ihr Skript ein:
726
Einrichtung
Hier liegt die Bibliothek in einem Unterverzeichnis lib. Ein Unterverzeichnis bietet sich hier an, da scriptaculous aus mehreren JavaScript-Dateien besteht und es sonst unübersichtlich wird. Prototype erweitert diverse JavaScript-Objekte wie Array, Boolean, Number, String und Function. Daneben kommen natürlich auch Browserobjekte wie Formulare (Objekt Form) zum Einsatz und es gibt auch eigene Objekte wie ein einfaches Template-System. Am bekanntesten ist Prototype aber für seine DOM-Erweiterungen. Beispielsweise können Sie mit folgender Zeile beliebigen HTML-Code nach einem Element einfügen:
25
26
new Insertion.After(ID, HTML);
Einige dieser Möglichkeiten lernen Sie in Abschnitt 26.3 »Nützliches in Prototype« kennen.
27
Der Zugriff auf DOM-Elemente wird über einfache Hilfsfunktionen erleichtert. Die simpelste ist die schon aus Xajax bekannte $-Funktion. Damit greifen Sie direkt auf ein Element zu:
28
$(ID)
Wenn Sie den Zugriff im lokalen Kontext einsetzen, erfolgt der Zugriff per Objekt global:
29
ajax.$(ID)
30
Diese Kurzsyntax ist allerdings nicht nur Ersatz für document.getElementById(). Statt der ID können Sie auch die Referenz auf ein Element übergeben. Und jedes Element, das hier übernommen wird, besitzt alle DOMErweiterungen von Prototype. Dazu gehören z.B. die Methoden show() und hide(), um das Element ein- und auszublenden: $('ausgabe').hide();
Hier ein einfaches Beispiel, das einen Absatz ausblendet: Listing 26.1:
Einsatz von Prototype (prototype_steuerung.html)
prototype Ausgabe
727
Prototype und scriptaculous
Abbildung 26.3: Sie sehen: nichts. Prototype hat das Element ausgeblendet.
scriptaculous erhalten Sie als separaten Download von der scriptaculousWebsite mit der spannenden URL http://script.aculo.us/. Die aktuelle scriptaculous-Version 1.7.0 basiert auf Prototype 1.5 oder höher. Im Archiv von scriptaculous ist die jeweilige Prototype-Version auch enthalten. Das Archiv besteht aus drei Verzeichnissen: lib enthält prototype.js, src die scriptaculous-JavaScript-Dateien und test den Unit-Test1, den scriptaculous bietet. Für die folgenden Beispiele packen wir alle scriptaculous-JavaScript-Dateien und die prototype.js in ein gemeinsames lib-Verzeichnis. Um scriptaculous einzubinden, verlinken Sie einfach die scriptaculous.js:
Damit werden allerdings alle untergeordneten Funktionen mit eingebunden. Wenn Sie nur bestimmte laden möchten, um Ladezeit zu sparen, können Sie dies mit dem GET-Parameter load erreichen. Fügen Sie dazu einfach die gewünschten Funktionen per Kommata getrennt aneinander:
Hier eine Übersicht über die verfügbaren Teile von scriptaculous: ■ ■ ■ ■ ■
builder enthält DOM-Möglichkeiten. effects beinhaltet Animationen und Effekte. dragdrop enthält die Drag&Drop-Steuerung. controls sind einige Ajax-Controls. slider bietet Schieberegler.
Neue Effekte und Funktionen werden nun über die entsprechenden Objekte hinzugefügt. Die Syntax gleicht dabei der von Prototype. Um beispielsweise ein Element einzublenden, verwenden Sie das Objekt Effect.Appear: new Effect.Appear('ausgabe', {duration: 4}); 1
728
Ein Unit-Test ist ein Test für die Funktionsfähigkeit einer Software-Komponente. In der testgetriebenen Entwicklung schreibt man zuerst den Test, dann die Komponente, die den Test erfüllen muss.
Ajax-Verbindung
Der erste Parameter ist die ID des Objekts, der zweite Parameter ein Objekt mit den Optionen. Hier ist die einzige Option die Dauer des Einblendens (duration), die vier Sekunden beträgt. Die Schreibweise für Optionen als Objekt entspricht ebenfalls der Konvention, die Prototype verwendet. Hier ein Beispiel, das ein Element zuerst per Prototype ausblendet und dann per scriptaculous-Effekt wieder anzeigt: Listing 26.2:
25
Der Einsatz von scriptaculous (scriptaculous.html)
prototype und scriptaculous Ausgabe
26 27
28
29
30
Abbildung 26.4: Der Text wird langsam eingeblendet.
26.2 Ajax-Verbindung Für die Ajax-Verbindung ist Prototype zuständig. Hier hat scriptaculous wie schon erwähnt nichts zu melden. Prototype besitzt ein Ajax-Objekt, das für alle Ajax-Verbindungen zuständig ist. Dieses Objekt wiederum besitzt mehrere Objekte für die Arbeit mit Ajax-Aufrufen: ■ ■
Ajax.Request sendet einen Ajax-Aufruf. Ajax.Responders erlaubt das Definieren von Event-Handlern für AjaxAufrufe (siehe Abschnitt 26.2.2 »Event-Handler«).
729
Prototype und scriptaculous
■ ■
Ajax.Updater aktualisiert ein Element per Ajax-Aufruf (vergleiche Abschnitt 26.2.3 »Prototype erweitern«). Ajax.PeriodicalUpdater ruft regelmäßig den Ajax.Updater auf, um ein Element zu aktualisieren (siehe Abschnitt 26.2.4 »Ajax.Updater und Ajax.PeriodicalUpdater«).
Die einfachste Möglichkeit, einen Ajax-Aufruf abzusenden, ist die Methode Ajax.Request(URL, Optionen). Sie besteht aus zwei Parametern, der URL mit dem Skript oder der Datei, die per Ajax aufgerufen wird, und einem Objekt mit den Optionen und Event-Handlern für die Ajax-Verbindung. Die Optionen finden Sie gesammelt unter Punkt 26.2.1, die Event-Handler in Abschnitt 26.2.2 »Event-Handler«. Fürs Erste ist nur eine wichtig, nämlich onSuccess. Damit legen Sie eine Funktion als Event-Handler fest, der aufgerufen wird, wenn die Ajax-Verbindung geklappt hat. Basis eines ersten Beispiels ist eine Textdatei, wie Sie sie schon aus dem grundlegenden Ajax-Kapitel kennen. Sie enthält nur einen einfachen Text: Listing 26.3: Das serverseitige »Skript« (daten.txt) Ausgabe per Prototype
TIPP
Prototype verfolgt bei der Ajax-Kommunikation einen anderen Ansatz als Xajax und Microsoft ASP.NET Ajax. Prototype ist ein rein JavaScript-basiertes und damit clientseitiges Framework. Das heißt auch, dass – im Gegensatz zu Xajax und Microsoft ASP.NET Ajax – der Entwickler durchaus mit JavaScript zu tun hat. Dafür kann er natürlich eine beliebige serverseitige Technologie verwenden, um mit dem Client zu kommunizieren. Der Ajax-Request wird mit dem Ajax.Request-Objekt von Prototype aufgerufen: new Ajax.Request( 'daten.txt', { onSuccess: ergebnisHandler } );
Der Event-Handler übernimmt das XMLHttpResponse-Objekt. Damit greifen Sie auf die XML- oder Textrückgabe zu: function ergebnisHandler(xml_http) { $('ausgabe').update(xml_http.responseText); }
Die Aktualisierung der Anzeige funktioniert hier sehr einfach über das angepasste Prototype-DOM. Mit $(ID) greifen Sie auf den -Block zu, mit der Methode update() weisen Sie ihm als neuen Inhalt die Textrückgabe des XMLHttpRequest-Objekts zu.
730
Ajax-Verbindung
Hier der vollständige Code: Listing 26.4: Ein einfacher Ajax-Request per Prototype (prototype_ajax.html) prototype
25
26 27
28
29
30
Abbildung 26.5: Die Ausgabe per Prototype
Prototype liefert einen besonderen HTTP-Header namens X-Request-With: XMLHttpRequest mit. Dieser kann in einem serverseitigen Skript verwendet werden, um festzustellen, ob es sich um einen normalen HTTP-Aufruf oder um einen XMLHttpRequest-Aufruf handelt. Ein weiterer Header gibt die Prototype-Version an (X-Prototype-Version).
TIPP
731
Prototype und scriptaculous
26.2.1 Optionen Wie bereits erwähnt, sind die Optionen für Prototype in einem Objekt versammelt. Die Eigenschaften des Objekts sind jeweils die Namen der Option, die Werte der Eigenschaft entsprechend dem Wert der Option. Die folgende Tabelle gibt einen Überblick über die verfügbaren Optionen. Tabelle 26.1: Mögliche Optionen
Option
Wert
Beschreibung
asynchronous
Boolean
Gibt an, ob XMLHttpRequest asynchron (true) oder synchron verwendet wird. Standardwert ist asynchron.
contentType
String
Gibt das HTTP-Feld Content-Type an, mit dem die Abfrage geschickt wird.
encoding
String
Gibt den Zeichensatz für die übermittelten Daten an. Standardwert ist UTF-8.
method
»get« oder »post«
Gibt die Versandmethode an. Je nach Versandmethode werden die Parameter unterschiedlich übertragen.
parameters
String oder Objekt
Enthält die Parameter für eine Übermittlung per GET oder POST. Wird für POST nur verwendet, wenn postBody nicht gesetzt ist. Besonders praktisch ist es, die Parameter als Objekt zu übergeben.
postBody
String
Enthält die Parameter für eine Übermittlung per POST. Die Angabe ist allerdings nicht als Objekt, sondern nur als String möglich.
requestHeaders
Objekt – Eigenschaften sind Header-Felder, Werte sind Feldwerte
Setzt HTTP-Header, die dann von Prototype verwendet werden. Sie ergänzen die Standardheader wie X-RequestWith, die Prototype sowieso setzt.
Array – gerader Index ist Header-Feld, der folgende ungerade Index der zugehörige Feldwert
Das folgende Beispiel zeigt, wie Sie als Versandmethode POST verwenden und Parameter an ein serverseitiges Skript übermitteln. Den Anfang macht das serverseitige Skript, das die Parameter übernimmt, prüft und addiert. Das Beispiel ist in PHP geschrieben, natürlich kann aber auch jede andere serverseitige Technologie eingesetzt werden. Listing 26.5: Addieren in PHP (summe.php)
732
Ajax-Verbindung
Basis des Beispiels ist ein einfaches, schon aus dem Xajax-Kapitel (Kapitel 25) bekanntes Formular, das die Eingabe von zwei Zahlen erlaubt: a b
25
Bei einem Klick auf die Versenden-Schaltfläche wird die Funktion addieren() aufgerufen. Sie startet den Ajax-Request. Als Versandmethode wird hier POST gewählt. GET wäre allerdings auch möglich. Um die Parameter zu übergeben, setzen Sie am besten die Option parameters ein, denn sie erlaubt es, Parameter als Objekt anzugeben:
26 27
parameters: { a: $F('a'), b: $F('b') }
28
Im Gegensatz dazu müssen Sie mit postBody den String selbst zusammensetzen. Der Zugriff auf die Formularwerte klappt hier direkt über die Hilfsmethode $F(ID). Sie liest den Wert (value-Attribut) eines Formularelements aus, dessen ID angegeben ist. Das Formularelement muss dementsprechend ein id-Attribut besitzen. Das name-Attribut funktioniert hier nicht.
29
30
Hier der zugehörige Code: Listing 26.6: Parameter per POST übergeben prototype
733
Prototype und scriptaculous
a b
Abbildung 26.6: Prototype addiert
26.2.2 Event-Handler In den Optionen lassen sich auch Event-Handler für verschiedene Arten von Ereignissen definieren. Der wichtigste Event-Handler ist onSuccess, denn er verarbeitet die (erfolgreiche) Rückgabe. Sie können als Event-Handler eine Funktionsreferenz angeben oder Sie fügen die Funktion direkt ein: Listing 26.7:
Event-Handler direkt in den Optionen (Ausschnitt aus:
prototype_ajax_eventhandler.html) new Ajax.Request( 'daten.txt', { onSuccess: function(xml_http) { $('ausgabe').update(xml_http.responseText); } } );
Eine weitere Besonderheit von Event-Handlern bei Prototype liegt darin, dass die entsprechenden Handler-Funktionen noch ein JSON-Element mitgeliefert bekommen können. Dieses Element wird aus dem HTTP-Feld X-JSON gespeist. Sie können dieses Feld per serverseitige Programmiersprache setzen. Hier ein PHP-basiertes Beispiel:
734
Ajax-Verbindung
Listing 26.8: Serverseitige Antwort inklusive JSON-Header