-
Notifications
You must be signed in to change notification settings - Fork 17
/
css.html
235 lines (215 loc) · 19.9 KB
/
css.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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>JavaScript: Zusammenarbeit mit CSS, Darstellung von Dokumenten steuern</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: Zusammenarbeit mit CSS, Darstellung von Dokumenten steuern</h1>
<div class="section" id="einleitung">
<h2>Einleitung</h2>
<p>Mit JavaScript können Sie die Darstellung des Dokuments dynamisch ändern, während es im Browser angezeigt wird. Dies ist ein wesentlicher Bestandteil der Interaktivität, die Sie einem Dokument mittels Javascript hinzufügen können. Die Möglichkeiten sind vielfältig: Beispielsweise können Sie als Reaktion auf eine Eingabe gewisse Elemente ein- und ausblenden. Es sind aber auch – mit entsprechendem Aufwand – visuelle Effekte und komplexe Animationen möglich.</p>
<p>Die Programmiersprache JavaScript besitzt keine eigenen Techniken, um die Gestaltung einer Webseite zu beeinflussen. Vielmehr besitzt JavaScript eine Schnittstelle zur Formatierungssprache Cascading Stylesheets (CSS). Mittels JavaScript können Sie also sämtliche Formatierungen vornehmen, die CSS möglich macht. Daher sollten Sie die Grundlagen von CSS bereits beherrschen, bevor Sie Dokumente mittels JavaScript umformatieren.</p>
<p>Das dynamische Ändern der <em>Darstellung</em> bildet einen großen Komplex in der JavaScript-Programmierung - ein anderer ist das dynamische Ändern der <em>Inhalte</em> über das <a href="dom.html">Document Object Model (DOM)</a>. Über das DOM können Sie Elemente hinzufügen oder löschen, Attributwerte setzen und Textinhalte einfügen oder verändern. In der Praxis gehen diese beiden Aufgaben - den Inhalt und dessen Darstellung modifizieren - oft miteinander einher.</p>
</div>
<div id="trennung-javascript-css" class="section">
<h2>Trennung von Layout-Regeln und JavaScript-Logik</h2>
<p>Bevor Sie die verschiedenen Möglichkeiten kennenlernen, wie Sie ein Element CSS-Formatierungen mithilfe von JavaScript verändern können, sollten Sie sich die Konzepte des <a href="einsatz.html">Unobtrusive JavaScript</a> in Erinnerung rufen.</p>
<p>Wenn Sie bereits einige Layouts mit CSS umgesetzt haben, sollten Sie die Aufgaben der Webtechniken kennen und deren sinnvolle Anwendung bereits beherrschen:</p>
<ul>
<li>Für die <em>Strukturierung der Inhalte</em> ist HTML zuständig. Sie wählen möglichst bedeutungsvolle HTML-Elemente. Für die Feinstruktur vergeben Sie Klassen und IDs.</li>
<li>Die <em>Präsentation</em> wird in ausgelagerten Stylesheets definiert. Diese sprechen gezielt Elemente im Dokument an und formatieren sie mit CSS-Eigenschaften. Elementnamen, IDs und Klassen bieten Angriffspunkte für CSS-Regeln.</li>
</ul>
<p>Diese Arbeitsweise hat enorme Vorteile bei der Webseiten-Entwicklung. Inhalt und Präsentation können unabhängig voneinander geändert werden. Mit wenig Aufwand kann die gewünschte Präsentation erzielt werden.</p>
<p>Wenn nun die JavaScript hinzutritt, sollten Sie dieses Modell konsequent fortführen. Orientieren Sie sich an folgenden Faustregeln:</p>
<ul>
<li>Definieren Sie die Formatierungsregeln im Stylesheet, nicht im JavaScript. Trennen sie den CSS-Anweisungen vom JavaScript-Code.</li>
<li>Sorgen Sie im JavaScript lediglich dafür, dass diese Formatierungsregeln angewendet werden – beispielsweise indem Sie einem Element eine Klasse hinzufügen. Durch diese Änderung der Klasse greift eine Regel im Stylesheet, deren Selektor die Klasse enthält.</li>
</ul>
<p>Sie können nicht nur ausgelagerte CSS-Regeln auf ein Element anwenden, sondern auch direkt CSS-Eigenschaften von einzelnen Elementen ändern. Dies entspricht dem <code>style</code>-Attribut in HTML. Diese Vermischung von HTML und CSS bzw. JavaScript und CSS sollten Sie möglichst vermeiden. Diese Direktformatierung ist nur in Sonderfällen sinnvoll und nötig.</p>
</div>
<div id="regeln-anwenden" class="section">
<h2>Stylesheet-Regeln auf ein Element anwenden</h2>
<p>Eine einfache Methode, um die Darstellung von Elementen per JavaScript zu ändern, ist das <strong>Hinzufügen einer Klasse</strong>. Die Formatierungen für diese Klasse wird im Stylesheet untergebracht. Damit wird der empfohlenen Trennung vom JavaScript-Code Genüge getan.</p>
<p>Betrachten wir als Beispiel ein JavaScript, dass die Eingaben eines Formulars überprüft. Beim Absenden des Formulars wird eine Handler-Funktion für das Ereignis <code>submit</code> aktiv. Diese Funktion soll fehlerhafte Formularfelder rot markieren.</p>
<p>Nehmen wir an, die <code>input</code>-Eingabefelder sind standardmäßig im Stylesheet so formatiert:</p>
<pre>
input {
padding: 4px 6px;
border: 1px solid #555;
background-color: #fafafa;
}
</pre>
<p>Im Stylesheet wird nun eine Regel definiert mit den Eigenschaften für fehlerhafte Felder. Wir nutzen dazu einen Selektor mit dem Elementnamen <code>input</code> kombiniert mit der Klasse <code>fehlerhaft</code>:</p>
<pre>input.fehlerhaft {
border-color: red;
background-color: #fff8f5;
}</pre>
<p>Am Anfang trifft der Selektor <code>input.fehlerhaft</code> auf kein Feld im Dokument zu. Um ein Eingabefeld umzuformatieren und die Regel anzuwenden, vergeben wir diese Klasse an das gewünschte <code>input</code>-Element.</p>
<p>Die Klassen eines Elements sind in JavaScript über die Eigenschaft <code>classList</code> des entsprechenden Elementobjekts zugänglich. <code>classList</code> ist listenartiges Objekt, das alle Klassen beinhaltet. Das Objekt bietet verschiedene Methoden an, um die Klassen eines Elements abzufragen und zu ändern.</p>
<p>Das Schema zum Hinzufügen einer Klasse lautet:</p>
<pre>element.classList.add('klassenname');</pre>
<p>Die beispielhafte Formularüberprüfung sieht so aus: Wir definieren eine Funktion <code>formularÜberprüfung</code>, die als Handler für das Ereignis <code>submit</code> registriert wird (siehe <a href="event-handling-grundlagen.html">Ereignis-Verarbeitung</a>). Die Funktion sucht das Formularfeld mittels <code>document.getElementById(…)</code> heraus. Ist der Wert des Feldes leer, wird eine Meldung ausgegeben und das Feld bekommt die Klasse <code>fehlerhaft</code>.</p>
<pre>
function formularÜberprüfung(event) {
// Suche das Formularfeld heraus und
// speichere es in eine Variable:
var element = document.getElementById('kontaktformular-name');
// Prüfe den Wert des Feldes:
if (element.value === '') {
// Zeige im Fehlerfall ein Hinweisfenster:
window.alert('Bitte geben Sie Ihren Namen an.');
// Weise dem Element die Klasse »fehlerhaft« zu:
<strong>element.classList.add('fehlerhaft');</strong>
// Setze den Fokus auf das Feld:
element.focus();
// Verhindere das Absenden des Formulars
// (unterdrücke die Standardaktion des Ereignisses):
event.preventDefault();
}
}
// Überwache das submit-Ereignis beim Formular
document.getElementById('kontaktformular')
.addEventListener('submit', formularÜberprüfung, false);
</pre>
<p>Der zugehörige HTML mit den nötigen IDs könnte so aussehen:</p>
<pre>
<form action="…" method="post" id="kontaktformular">
<p>
<label>
Ihr Name:
<input type="text" name="name" id="kontaktformular-name">
</label>
</p>
<em>… weitere Felder …</em>
<p><button>Absenden</button></p>
</form>
</pre>
<p>Das Hinzufügen der Klasse an einem Element ist nur eine minimale JavaScript-Änderung. Sie führt dazu, dass eine Regel aus dem Stylesheet auf bestimmte Elemente greift. Der Browser wendet daraufhin die definierten Formatierungen an.</p>
<p>Damit können Sie auch komplexere Aufgabenstellungen lösen, denn Ihnen stehen alle Möglichkeiten von CSS-Selektoren zu Verfügung. Beispielsweise können Sie mittels Nachfahrenselektoren Elemente formatieren, die unterhalb des mit der Klasse markierten Elements liegen. So können Sie durch das Ändern einer Klasse gleich mehrere, im DOM-Baum unterhalb liegende Elemente formatieren, ohne diese einzeln anzusprechen.</p>
<p>TODO: Beispiel dazu. Mit Nachfahrenselektoren größere Umformatierungen vornehmen, ohne alle Elemente einzeln anzusprechen</p>
<div class="section" id="classlist">
<h3>Hinzufügen, Löschen und Abfragen von Klassen</h3>
<p>Das <code>classList</code>-Objekt erlaubt es nicht nur, eine Klasse hinzuzufügen, sondern auch sie zu löschen, zu ersetzen und ihr Vorhandensein abzufragen. Hier eine kleine Übersicht:</p>
<ul>
<li><code>element.classList.add('klasse')</code> fügt eine Klasse hinzu.</li>
<li><code>element.classList.remove('klasse')</code> löscht eine Klasse.</li>
<li><code>element.classList.toggle('klasse')</code> fügt eine Klasse hinzu, falls sie noch nicht vorhanden ist, oder löscht sie, falls sie schon vorhanden ist.</li>
<li><code>element.classList.contains('klasse')</code> gibt zurück, ob eine Klasse vorhanden ist. Die Funktion gibt einen Boolean-Wert zurück.</li>
<li><code>element.classList.replace('klasse1', 'klasse2')</code> ersetzt eine Klasse mit einer anderen, sofern sie vorhanden ist.</li>
</ul>
</div>
<div id="style-objekt" class="section">
<h2>Direktformatierung über das <code>style</code>-Objekt</h2>
<div id="html-inline-styles" class="section">
<h3>Inline-Styles in HTML</h3>
<p>Zur direkten CSS-Formatierung eines HTML-Elements existiert das <code>style</code>-Attribut. Es ist eine Zuordnungsliste mit Eigenschaft-Wert-Paaren. Ein HTML-Beispiel:</p>
<pre><p style="color: red; background-color: yellow; font-weight: bold;">Fehler!</p></pre>
<p>Gegenüber dem Einsatz eines Stylesheets sind diese <em>Inline-Styles</em> (eingebettete Formatierungen) ineffektiv und führen zu zu einer Vermischung von HTML und CSS, die die Wartbarkeit des Dokuments verschlechtert. Sie sollten Sie daher nur in Ausnahmefällen einsetzen, auf die wir später noch zu sprechen kommen.</p>
</div>
<div id="style-objekt-inline-styles" class="section">
<h3>Das <code>style</code>-Objekt als Schnittstelle zu Inline-Styles</h3>
<p>JavaScript bietet eine Schnittstelle zu diesem <code>style</code>-Attribut: Das <code>style</code>-Objekt bei jedem Elementobjekt. Das <code>style</code>-Objekt hat für jede mögliche CSS-Eigenschaft eine entsprechende Objekteigenschaft. Der CSS-Eigenschaft <code>color</code> entspricht also eine Objekteigenschaft <code>element.style.color</code> vom Typ String.</p>
<p>CSS-Eigenschaftsnamen mit Bindestrichen, wie z.B. <code>background-color</code>, können nicht unverändert als JavaScript-Eigenschaftsnamen übernommen werden. Deshalb werden sie im sogenannten <em>Camel-Case</em> (Groß- und Kleinschreibung im Kamel-Stil) notiert: Der Bindestrich fällt weg, dafür wird der darauf folgende Buchstabe zu einem Großbuchstaben. Aus <code>background-color</code> wird also <code>backgroundColor</code>, aus <code>border-left-width</code> wird <code>borderLeftWidth</code> und so weiter. Die Großbuchstaben in der Wortmitte werden mit Höcker eines Kamels verglichen.</p>
<p>Folgendes Beispiel veranschaulicht das Setzen der Hintergrundfarbe eines Elements auf rot:</p>
<pre>document.getElementById('beispielID').style.backgroundColor = 'red';</pre>
<p>Als Werte müssen Sie stets Strings angeben. Darin muss ein gültiger Eigenschaftswert stehen. Das gilt auch für Zahlenwerte, die eine Einheit erfordern:</p>
<pre>
element.style.marginTop = 15; // Falsch!
element.style.marginTop = '15px'; // Richtig
</pre>
</div>
<div id="style-eigenschaftsnamen" class="section">
<h3>Besondere JavaScript-Eigenschaftsnamen</h3>
<p>Es gibt einen Sonderfall bei der Übersetzung von CSS-Eigenschaftsnamen in JavaScript-Eigenschaftsnamen. Das betrifft die CSS-Eigenschaft <code>float</code>. Dieser Name war einmal ein reserviertes Wort in JavaScript, d.h. er konnte nicht als <a href="syntax.html#bezeichner">Bezeichner</a> verwendet werden.</p>
<p>Auch wenn das mittlerweile kein Problem mehr ist, sollten Sie statt <code>float</code> die JavaScript-Eigenschaft <code>cssFloat</code> notieren, also z.B. <code>element.style.cssFloat</code>.</p>
</div>
<div id="style-objekt-sinnvoll" class="section">
<h3>Sinnvoller Einsatz des <code>style</code>-Objekts</h3>
<p>Das Setzen von CSS-Formatierungen direkt über das <code>style</code>-Objekt ist zwar einfach. Doch Sie sollten diese Präsentationsinformationen wie gesagt nicht im JavaScript, sondern in einer Regel im Stylesheet unterbringen. Die Verwendung von Inline-Styles ist nur dann notwendig, wenn der Eigenschaftswert nicht fest steht, sondern im JavaScript berechnet wird. Das ist der Fall z.B. bei Animationen oder bei einer Positionierung abhängig von der Mauszeiger-Position wie.</p>
</div>
</div>
<div id="werte-auslesen" class="section">
<h2>Berechnete CSS-Eigenschaften auslesen</h2>
<p>Das <code>style</code>-Objekt wird immer wieder missverstanden. <strong>Sie können über das <code>style</code>-Objekt <strong>nicht</strong> den aktuellen, berechneten Wert einer CSS-Eigenschaft auslesen.</strong> Sie können damit lediglich Inline-Styles setzen und die bereits gesetzten auslesen.</p>
<p>Die Objekteigenschaften (<code>element.style.<var>eigenschaft</var></code>) sind allesamt <strong>leer</strong>, wenn sie per HTML-Inline-Style (<code>style</code>-Attribut) oder wie beschrieben mit JavaScript gesetzt wurden. Folgendes Beispiel verdeutlicht dies:</p>
<pre>
<p id="ohne-inline-styles">Element ohne Inline-Styles</p>
<p id="mit-inline-styles" style="color: red">Element mit Inline-Styles</p>
</pre>
<pre>
// Gibt einen leeren String aus:
window.alert(
document.getElementById('ohne-inline-styles').style.backgroundColor
);
// Gibt »red« aus, weil Inline-Style gesetzt wurde:
window.alert(
document.getElementById('mit-inline-styles').style.backgroundColor
);
</pre>
<p>Wenn man in Erfahrung bringen will, welche tatsächliche Textfarbe oder welche Pixel-Breite ein Element hat, dann ist nach den sogenannten <em>berechneten Werten</em> (englisch <em>computed values</em>) gefragt.</p>
<div class="section" id="getcomputedstyle">
<h3><code>getComputedStyle</code></h3>
<p>Der Standard, der die Schnittstelle zwischen JavaScript und CSS festlegt, definiert die globale Funktion <code>window.getComputedStyle()</code>. Sie erwartet ein Elementobjekt als ersten Parameter und einen String mit einem CSS-Pseudo-Element als zweiten Parameter (beispielsweise <code>'::after'</code> oder <code>'::before'</code>). Sofern Sie nicht die Formatierung des Pseudo-Elements abfragen wollen, lassen Sie den zweiten Parameter einfach weg.</p>
<p><code>getComputedStyle()</code> gibt ein Objekt zurück, das genauso aufgebaut ist wie das bereits besprochene <code>style</code>-Objekt. Es enthält für jede CSS-Eigenschaft eine entsprechende Objekteigenschaft mit dem aktuellen berechneten Wert.</p>
<pre>
var element = document.getElementById('beispielID');
var computedStyle = window.getComputedStyle(element);
window.alert('Textfarbe: ' + computedStyle.color);
window.alert('Elementbreite: ' + computedStyle.width);
</pre>
<p>Die berechneten Werte (<em>computed values</em>) sind nicht identisch mit den Werten, die Sie im Stylesheet notiert haben. Die Werte in <code>margin-top: 2em;</code> und <code>font-size: 120%;</code> werden in Pixelwerte umgerechnet, sodass <code>getComputedStyle()</code> Werte mit der Einheit <code>px</code> zurückgibt.</p>
<p>Auch beispielsweise bei Farbwerten können Sie nicht erwarten, dass das Format des berechneten Wertes mit dem des Stylesheets übereinstimmt. Denn es gibt in CSS verschiedene Formate, um Farben zu notieren. Notieren Sie im Stylesheet beispielsweise <code>color: red</code>, so kann es sein, dass <code>getComputedStyle()</code> für <code>color</code> den Wert <code>'rgb(255, 0, 0)'</code> liefert. Dies ist derselbe Wert in einer anderen Schreibweise.</p>
</div>
<div class="section" id="elementgroessen">
<h3>Elementbox-Größen auslesen</h3>
<p>Sie können <code>getComputedStyle()</code> nutzen, um die aktuelle berechnete Höhe (<code>width</code>) und Breite <(<code>height</code>) einer Element-Box auszulesen. Die Methode gibt dann einen String-Wert wie <code>'560px'</code> zurück. Beim Standard-Box-Modell beziehen sich die CSS-Eigenschaft <code>width</code> und <code>height</code> auf die Innengröße. Das heißt Innenabstand (<code>padding</code>), Rahmen (<code>border</code>) und gegebenenfalls Außenrahmen (<code>margin</code>) müssen hinzugefügt werden, um die vollständige Höhe und Breite des Elements zu berechnen.</p>
<p>Es gibt eine Reihe vom Eigenschaften der Elementobjekte, die diese Berechnungen vereinfachen. Im Gegensatz zu <code>getComputedStyle()</code> geben sie keine String-Werte samt Einheiten zurück, sondern direkt JavaScript-Ganzzahlen (Number-Werte) in der Einheit Pixel.</p>
<dl>
<dt><code>offsetWidth</code> und <code>offsetHeight</code></dt>
<dd>liefern die Breite bzw. Höhe der Rahmen-Box des Elements. Das bedeutet, dass der Innenabstand (<code>padding</code>) und der Rahmen (<code>border</code>) inbegriffen sind, der Außenrahmen hingegen (<code>margin</code>) nicht.</dd>
<dt><code>clientWidth</code> und <code>clientHeight</code></dt>
<dd>liefern Breite bzw. Höhe der Innenabstand-Box des Elements. Das bedeutet, dass <code>padding</code> inbegriffen ist, während <code>border</code> und <code>margin</code> nicht eingerechnet werden. Ebenso wird die Größe einer möglicherweise angezeigte Bildlaufleiste (Scrollbar) nicht einberechnet.</dd>
<dt><code>scrollWidth</code> und <code>scrollHeight</code></dt>
<dd>geben die tatsächlich angezeigte Breite bzw. Höhe des Inhalts wieder. Wenn das Element kleiner ist, als der Inhalt es erfordert, also Bildlaufleisten angezeigt werden, so geben diese Eigenschaften die Größe des des aktuell sichtbaren Ausschnittes wieder.</dd>
</dl>
<p>In den meisten Fällen werden Sie die äußere Größe eines Elements benötigen, also <code>offsetWidth</code> und <code>offsetHeight</code>. Das folgende Beispiel gibt die Größe eines Elements aus:</p>
<pre>var element = document.getElementById('beispielID');
window.alert('Breite: ' + element.offsetWidth + ' Höhe: ' + element.offsetHeight);</pre>
</div>
<div class="section" id="getboundingclientrect">
<h3>getBoundingClientRect</h3>
<p>…</p>
</div>
</div>
<div class="section" id="stylesheets-aendern">
<h2>Zugriff auf die eingebundenen Stylesheets</h2>
<div class="section">
<h3>Stylesheets mit JavaScript deaktivieren und einfügen</h3>
<p>…</p>
</div>
<div class="section">
<h3>Stylesheet-Regeln dynamisch erzeugen und ändern</h3>
<p>…</p>
</div>
</div>
<div class="sequence-navigation">
<p class="next"><a href="sicherheit.html" rel="next">Zusammenarbeit mit CSS</a></p>
<p class="prev"><a href="fenster.html" rel="prev">Fenster und Dokumente</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>