-
Notifications
You must be signed in to change notification settings - Fork 17
/
browser.html
202 lines (192 loc) · 16.3 KB
/
browser.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>JavaScript: Browserübergreifende Entwicklung</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="js-doku.css">
</head>
<body>
<div id="nav">
<p>Hier entsteht eine <strong>JavaScript-Dokumentation</strong> von <a href="https://molily.de/">molily</a>. Derzeit ist sie noch lückenhaft, wächst aber nach und nach. Kommentare und Feedback werden gerne per <a href="mailto:[email protected]">E-Mail</a> entgegen genommen.</p>
<p class="contents-link"><a href="./">Zum Inhaltsverzeichnis</a></p>
</div>
<h1>JavaScript: Browserübergreifende Entwicklung</h1>
<div class="section" id="browserwirklichkeit">
<h2>JavaScript und die Browser-Wirklichkeit</h2>
<p>Vor einigen Jahren bestand ein großer Teil der Webentwicklung daraus, Unterschiede und Fehler der Browser zu berücksichtigen. Besonders im Bereich CSS gab es lange unterschiedliche Darstellungen. Nach und nach haben die großen Browser ihre Fehler korrigiert und es ist einfacher geworden, ein CSS-Layout zu erstellen.</p>
<p>Wie sieht es im Bereich JavaScript aus? Die großen Browser setzen die DOM-Standards weitgehend um. Nach und nach wurden undokumentierte Techniken standardisiert und Fehler bei der Umsetzung dieser Standards behoben.</p>
<p>Dennoch entstehen ständig neue Techniken, die noch nicht breit unterstützt werden. Für einige Aufgaben sind daher <strong>mehrgleisige Scripte</strong> nötig, die dieselbe Aufgabe je nach Browserfähigkeiten auf eine unterschiedliche Weise lösen.</p>
<p>Glücklicherweise ist es in JavaScript möglich, gezielt die <strong>Existenz von Objekten abzufragen</strong>. Existiert ein Objekt und hat gegebenenfalls einen bestimmten Typ und Wert, kann damit gearbeitet werden. Andernfalls greifen Alternativlösungen.</p>
<p>Eine solche <strong>Fähigkeiten-Weiche</strong> macht zuverlässige und zukunftsfähige Scripte möglich. Sie ist besser als eine <em>Browserweiche</em>, die lediglich über den Browsernamen Rückschlüsse zieht.</p>
<p>Mittlerweile wurden die wichtigsten Unterschiede und Fehler der verbreiteten Browser dokumentiert. Dabei entstanden fertige Scripte und bewährte Verfahren, mit denen sich <strong>Standardaufgaben browserübergreifend lösen</strong> lassen.</p>
<p>Dieser Ausblick soll optimistisch stimmen und dazu anregen, die Herausforderung der Browser-Wirklichkeit anzunehmen. Es ist jedoch harte Arbeit, sich in die browserübergreifende JavaScript-Entwicklung hineinzudenken.</p>
</div>
<div id="abwaertskompatibilitaet" class="section">
<h2>Abwärtskompatibilität und Zukunftsfähigkeit</h2>
<p>Was bedeutet eigentlich browserübergreifende Entwicklung? Üblicherweise wird darunter verstanden: Ein Script soll in allen Browsern den gewünschten Zweck erfüllen und dasselbe leisten. Von dieser einseitigen Vorstellung sollten Sie sich verabschieden, denn in vielen Fällen ist sie schlicht nicht umsetzbar.</p>
<p>Ein realistisches Ziel ist, die vergangenen, gegenwärtigen und zukünftigen Browser <strong>gemäß ihrer jeweiligen Fähigkeiten</strong> zu bedienen. Dies bedeutet, dass ihr Script nicht auf allen Browsern exakt denselben Effekt haben muss. Es ist nicht sinnvoll, technisch veralteten oder wenig verbreiteten Browser mit unverhältnismäßigem Aufwand dasselbe Ergebnis zu liefern.</p>
<p>Durch geschickte Programmierung ist es meistens möglich, allen relevanten Browsern eine funktionsfähige Seite zu präsentieren. In älteren Browsern ist die Seite dann weniger ansehnlich und komfortabel bedienbar wie neueren Browsern.</p>
<p><em>Abwärtskompatibilität</em> bedeutet, dass Ihr Script auf den Fall vorbereitet ist, dass gewisse JavaScript-Techniken nicht zur Verfügung steht oder von Seiten des Browsers fehlerhaft umgesetzt sind. <em>Zukunftsfähigkeit</em> bedeutet, dass Sie durchaus neue und noch nicht breit unterstützte Techniken verwenden können. Voraussetzung ist in beiden Fällen, dass sie keine Techniken stillschweigend voraussetzen, sondern immer prüfen, ob die benötigten Objekte existieren und die Anweisungen ihres Programmes die erwarteten Ergebnisse liefern.</p>
</div>
<div id="fallunterscheidungen" class="section">
<h2>Fallunterscheidungen und Vereinheitlichungen</h2>
<p>Die Grundstruktur der browserübergreifenden Programmierung ist der mehrgleisiger Ablauf. Scripte nutzen sie immer wieder:</p>
<pre>
Wenn die nötige Technik zur Verfügung steht,
Dann:
Löse das Problem auf die eine Weise
Andernfalls:
Wenn die Alternativtechnik zur Verfügung steht,
Dann:
Löse die Aufgabe auf eine andere Weise
</pre>
<p>Diese Fallunterscheidungen werden in JavaScript üblicherweise mit bedingten Anweisungen umgesetzt: <code>if (Bedingung) { Anweisungen } else { Anweisungen }</code></p>
<p>Entscheidend ist, genau die Unterschiede zu kennen und <em>punktuell</em> solche Abzweigungen einzubauen. Nicht das ganze Script sollte eine solche Struktur haben. Es sollte verzweigten Abläufe nur dort nutzen, <em>wo es nötig ist</em>. Auf diese Weise sparen Sie sich doppelte Arbeit.</p>
<p>Wo Unterschiede auftreten, sollten Sie mit solchen Abfragen möglichst für <strong>Vereinheitlichung</strong> sorgen. Die nachfolgenden Anweisungen müssen sich dann um die Unterschiede keine Gedanken mehr machen. Bis zur nächsten Fallunterscheidung folgt Code, der von allen Browsern verstanden wird.</p>
<p>Nehmen wir ein Beispiel aus dem Event-Handling. Wie wir im Kapitel <a href="event-handling-objekt.html">Arbeiten mit dem Event-Objekt</a> gelernt, bekommt eine Handler-Funktion das Event-Objekt als Parameter übergeben. Im Internet Explorer vor Version 9 ist es stattdessen über <code>window.event</code> zugänglich. Über das Event-Objekt hat man Zugriff auf das Zielelement. Auch in dem Punkt unterscheiden sich die Browser: Das Element ist entweder in der Eigenschaft <code>target</code> (DOM-konforme Browser) oder <code>srcElement</code> (Internet Explorer < 9) gespeichert. Nun könnte man folgendermaßen vorgehen:</p>
<pre>
function handlerFunktion(event) {
if (event) {
alert('Element, an dem das Ereignis passierte: ' + event.target.nodeName);
} else if (window.event) {
alert('Element, an dem das Ereignis passierte: ' + window.event.srcElement.nodeName);
}
}
</pre>
<p>Allerdings ist dieser Code redundant. Anstatt direkt browserspezifischen Code zu schreiben, nutzen wir <strong>punktuelle Vereinheitlichung</strong>, die gleiche Voraussetzungen für das Script schafft. Nachdem die Browserunterschiede eingeebnet wurden, können wir die eigentliche Aufgabe einfach umsetzen. Der Code wird übersichtlicher: Die Bereiche, die sich den Browser-Unterschieden widmen, sind getrennt von denen, die die eigentliche Aufgabe lösen.</p>
<pre>
function handlerFunktion(event) {
// Vereinheitliche den Zugriff auf das Event-Objekt:
if (!event) {
event = window.event;
}
// Das Event-Objekt ist nun browserübergreifend in der Variable 'event' gespeichert.
// Vereinheitliche den Zugriff auf das Zielelement :
var target;
if (event.target) {
target = event.target;
} else if (event.srcElement) {
target = event.srcElement;
}
// Das Zielelement ist nun browserübergreifend in der Variable 'target' gespeichert.
// Nach der Vereinheitlichung folgt die eigentliche Umsetzung.
window.alert('Element, an dem das Ereignis passierte: ' + target.nodeName);
}
</pre>
<p>Diese Umsetzung mag zunächst länger und umständlicher scheinen. Das liegt jedoch bloß an der ausführlichen Schreibweise. Eine mögliche Kurzschreibweise haben wir bereits kennengelernt:</p>
<pre>
function handlerFunktion(event) {
// Vereinheitlichung
event = event || window.event;
var target = event.target || event.srcElement;
// Eigentliche Umsetzung
window.alert('Element, an dem das Ereignis passierte: ' + target.nodeName);
}
</pre>
</div>
<div id="faehigkeitenerkennung" class="section">
<h2>Fähigkeitenerkennung statt Browsererkennung</h2>
<p>Lange Zeit bedienten sich browserübergreifende JavaScripte einer sogenannten Browsererkennung. Anstatt die konkreten Unterschiede in Erfahrung zu bringen, fragte man kurzerhand den <strong>Browsernamen</strong> ab. Die Struktur einer solchen Browserweiche sah etwa so aus:</p>
<pre>
Wenn der Browser den Namen »Internet Explorer« hat,
Dann:
Löse die Aufgabe auf die die IE-typische Weise
Andernfalls:
Löse die Aufgabe auf die Netscape-typische Weise
</pre>
<p>Diese Browserweichen nutzen das JavaScript-Objekt <code>window.navigator</code>, das verschiedene Informationen über den Browser liefert, der das JavaScript ausführt.</p>
<p>Das obige Beispiel berücksichtigt allein die beiden Browser Internet Explorer und Netscape Navigator. Diese standen sich Ende der 1990er-Jahre gegenüber und beherrschten den Browsermarkt. Diese Vorgehensweise ging so lange gut, wie nur diese beiden Browser verbreitet waren und sich deren Versionen gleich verhielten. Diese Situation war nur für eine kurze Zeit gegeben. Danach funktionierten solche Scripte nicht mehr zuverlässig.</p>
<p>Alternativ zur Abfrage des Browsernamens wurden <strong>zentrale Objekte</strong> zur Browsererkennung verwendet:</p>
<pre>
Wenn das Objekt document.all existiert,
Dann:
Nimm an, es ist ein Internet Explorer und löse die Aufgabe
auf die die IE-typische Weise
Andernfalls:
Wenn das Objekt document.layers existiert,
Dann:
Nimm an, es ist ein Netscape Navigator und löse die Aufgabe
auf die Netscape-typische Weise
</pre>
<p>Solche Objektabfragen waren nicht abwegig, denn die abgefragten Objekte <code>document.all</code> und <code>document.layers</code> wurden bei der Umsetzung meistens auch verwendet. Wenn jedoch von der Existenz <em>eines</em> Objekts stillschweigend auf die Existenz vieler anderer Browserfähigkeiten geschlossen wird, handelt es sich um eine versteckte Browserabfrage.</p>
<p>Eine Browserweiche geht davon aus, dass der Browser eine Reihe von Techniken unterstützt, nur weil er einen bestimmten Namen trägt oder ein zentrales Objekt existiert.</p>
<p>Zum einen können damit immer nur die derzeit bekannten Browser in ihren aktuellen Versionen berücksichtigt werden. Zum anderen halten sich Browser an herstellerunabhängige Standards. Aber auch ursprünglich browserspezifische Erfindungen sind nicht mehr auf einen Browser begrenzt. Andere Hersteller haben sie ebenfalls übernommen.</p>
<p>Browserweichen sind daher nicht zuverlässig und zukunftsfähig. Browserweichen können prinzipiell nicht alle Fälle angemessen berücksichtigen. Sie sollten sie möglichst vermeiden und stattdessen abfragen, ob der jeweilige Browser <em>die Techniken unterstützt</em>, die Sie <em>tatsächlich in Ihrem Script verwenden</em>.</p>
</div>
<div id="objektabfragen" class="section">
<h2>Objektabfragen</h2>
<div id="objekte-methoden" class="section">
<h3>Objekte und Methoden abfragen</h3>
<p>Die einzelnen Fähigkeiten eines Browsers drücken sich meist darin aus, dass bestimmte vordefinierte Objekte, Eigenschaften bzw. Methoden existieren. In manchen Fällen müssen Sie zusätzlich prüfen, ob die Eigenschaft auch einen bestimmten Typ oder Wert hat.</p>
<p>Wenn wir eine bedingte Anweisung mit <code>if (…) {…}</code> notieren, so wird die Bedingung (der Ausdruck zwischen den runden Klammern) letztlich in einen <a href="kernobjekte.html#boolean">Boolean-Wert</a> umgewandelt, also <code>true</code> oder <code>false</code>. Objekte ergeben bei der Umwandlung in dem Typ <code>Boolean</code> den Wert <code>true</code>.</p>
<p>Das bedeutet, Sie können <code>if (objekt.eigenschaft) { … }</code> notieren, um die Existenz eines Untero abzufragen. Für Funktionsobjekte gilt dasselbe, also notieren wir <code>if (objekt.methode) { … }</code>.</p>
<p>Die Schreibweise <code>objekt.eigenschaft</code> bzw. <code>objekt.methode</code> ist dabei entscheidend. Wenn die Eigenschaft oder Methode nämlich nicht existiert, ergibt der Ausdruck schlicht <code>undefined</code>. Die Bedingung ist damit nicht erfüllt.</p>
<p>Das folgende Beispiel veranschaulicht die Existenzabfrage von Objekten und Methoden. Die Funktion bringt den aktuell markierten Text im Dokument in Erfahrung und gibt diesen in einem Meldungsfenster aus.</p>
<pre>
function selektierterText() {
var text = '';
if (window.getSelection) {
text = window.getSelection();
} else if (document.selection && document.selection.createRange) {
text = document.selection.createRange().text;
} else {
return;
}
window.alert(text);
}
</pre>
<p>Es existieren für diese Aufgabenstellung <strong>zwei Lösungswege</strong>, die in unterschiedlichen Browsern zum Ziel führen. Das Beispiel demonstriert daher eine Fallunterscheidung mit verschachtelten <code>if</code>-<code>else</code>-Anweisungen.</p>
<p>Es werden die Objekte verwendet, die zur Verfügung stehen. Kennt der JavaScript-Interpreter keines dieser Objekte, wird die Funktion vorzeitig beendet. Die Fallunterscheidung in verständlicher Sprache:</p>
<pre>
Existiert die Methode window.getSelection?
Falls ja:
Rufe diese Methode auf.
Falls nein:
Existiert das Objekt document.selection und hat es eine Methode createRange?
Falls ja:
Rufe diese Methode auf und greife auf die Eigenschaft text zu.
Falls nein:
Brich ab.
</pre>
<p>Wie Sie sehen, kommt dieses Beispiel ganz ohne Browserabfragen aus und ist doch für verschiedene Browser ausgelegt.</p>
</div>
<div class="section" id="andere-typen-abfragen">
<h3>Andere Typen abfragen</h3>
<p>Wenn Sie die Existenz von Nicht-Objekten prüfen wollen, müssen Sie anders vorgehen.</p>
<p>Zahlen (Typ <code>Number</code>) und Zeichenketten (Typ <code>String</code>) können Sie zwar auch mit <code>if (objekt.numberEigenschaft)</code> bzw. <code>if (objekt.stringEigenschaft)</code> abfragen. Die Bedingung in den Klammern wird aber wie gesagt in einen Boolean-Wert umgewandelt. Leere Zeichenketten (<code>''</code>) und manche Zahlenwerte (z.B. <code>0</code>) ergeben bei dieser Umwandlung <code>false</code>.</p>
<p>Diese Umwandlungsregeln können Verwirrung stiften: Manchmal ist <code>0</code> ein gültiger Wert und Sie können damit arbeiten. In anderen Fällen weist er darauf hin, dass der Browser die benötigte Fähigkeit nicht hat und Sie die abgefragte Eigenschaft nicht verwenden können.</p>
<p>Wenn Sie mit solchen Typen umgehen, sollten Sie daher auf den <strong>Operator <code>typeof</code></strong> ausweichen. Dieser gibt den Typ einer Eigenschaft als String zurück. In den beschriebenen Fällen wäre das <code>"string"</code> bzw. <code>"number"</code>). Ein Beispiel ist die Abfrage der Existenz von <code>window.innerHeight</code>, welche die Höhe des Anzeigebereichs des Browsers als Zahl enthält:</p>
<pre>
if (<strong>typeof window.innerHeight === 'number'</strong>) {
window.alert('Der Anzeigebereich des Browsers ist ' +
window.innerHeight + ' Pixel hoch!');
}
</pre>
<p>Manche Browser, insbesondere alte Internet-Explorer-Versionen, geben den den Typ nicht immer korrekt wieder. Daher hat es sich eingebürgert, bei Existenzabfragen bloß zu prüfen, ob <code>typeof</code> nicht <code>'undefined'</code> liefert:</p>
<pre>
if (<strong>typeof window.innerHeight !== 'undefined'</strong>) {
window.alert('Der Anzeigebereich des Browsers ist ' +
window.innerHeight + ' Pixel hoch!');
}</pre>
<div class="references">
<h3>Weiterführende Lektüre</h3>
<ul>
<li><a href="https://wiki.selfhtml.org/wiki/JavaScript/Tutorials/OOP/Objektabfrage">Objektabfragen und Fallunterscheidungen in JavaScript</a></li>
</ul>
</div>
</div>
<div class="sequence-navigation">
<p class="next"><a href="fenster.html" rel="next">Fenster und Dokumente</a></p>
<p class="prev"><a href="event-handling-effizient.html" rel="prev">Effiziente Ereignis-Verarbeitung</a></p>
</div>
<div id="footer">
<p><strong>JavaScript-Dokumentation</strong> · <a href="./">Zum Inhaltsverzeichnis</a></p>
<p>Autor: <a href="https://molily.de/">molily</a> · Kontakt: <a href="mailto:[email protected]">[email protected]</a></p>
<p>Lizenz: <a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/de/">Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen 3.0</a></p>
<p><a href="https://github.com/molily/javascript-einfuehrung">JavaScript-Einführung auf Github</a></p>
<p>Kostenloses Online-Buch und E-Book:<br><a href="https://testing-angular.com" lang="en" hreflang="en">Testing Angular – A Guide to Robust Angular Applications</a> (englisch)</p>
</div>
<script src="js-doku.js"></script>
</body>
</html>