JavaScript: Zusammenarbeit mit CSS, Darstellung von Dokumenten steuern

Einleitung

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.

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.

Das dynamische Ändern der Darstellung bildet einen großen Komplex in der JavaScript-Programmierung - ein anderer ist das dynamische Ändern der Inhalte über das Document Object Model (DOM). Ü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.

Trennung von Layout-Regeln und JavaScript-Logik

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 Unobtrusive JavaScript in Erinnerung rufen.

Wenn Sie bereits einige Layouts mit CSS umgesetzt haben, sollten Sie die Aufgaben der Webtechniken kennen und deren sinnvolle Anwendung bereits beherrschen:

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.

Wenn nun die JavaScript hinzutritt, sollten Sie dieses Modell konsequent fortführen. Orientieren Sie sich an folgenden Faustregeln:

Sie können nicht nur ausgelagerte CSS-Regeln auf ein Element anwenden, sondern auch direkt CSS-Eigenschaften von einzelnen Elementen ändern. Dies entspricht dem style-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.

Stylesheet-Regeln auf ein Element anwenden

Eine einfache Methode, um die Darstellung von Elementen per JavaScript zu ändern, ist das Hinzufügen einer Klasse. Die Formatierungen für diese Klasse wird im Stylesheet untergebracht. Damit wird der empfohlenen Trennung vom JavaScript-Code Genüge getan.

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 submit aktiv. Diese Funktion soll fehlerhafte Formularfelder rot markieren.

Nehmen wir an, die input-Eingabefelder sind standardmäßig im Stylesheet so formatiert:

input {
  padding: 4px 6px;
  border: 1px solid #555;
  background-color: #fafafa;
}

Im Stylesheet wird nun eine Regel definiert mit den Eigenschaften für fehlerhafte Felder. Wir nutzen dazu einen Selektor mit dem Elementnamen input kombiniert mit der Klasse fehlerhaft:

input.fehlerhaft {
   border-color: red;
   background-color: #fff8f5;
}

Am Anfang trifft der Selektor input.fehlerhaft auf kein Feld im Dokument zu. Um ein Eingabefeld umzuformatieren und die Regel anzuwenden, vergeben wir diese Klasse an das gewünschte input-Element.

Die Klassen eines Elements sind in JavaScript über die Eigenschaft classList des entsprechenden Elementobjekts zugänglich. classList ist listenartiges Objekt, das alle Klassen beinhaltet. Das Objekt bietet verschiedene Methoden an, um die Klassen eines Elements abzufragen und zu ändern.

Das Schema zum Hinzufügen einer Klasse lautet:

element.classList.add('klassenname');

Die beispielhafte Formularüberprüfung sieht so aus: Wir definieren eine Funktion formularÜberprüfung, die als Handler für das Ereignis submit registriert wird (siehe Ereignis-Verarbeitung). Die Funktion sucht das Formularfeld mittels document.getElementById(…) heraus. Ist der Wert des Feldes leer, wird eine Meldung ausgegeben und das Feld bekommt die Klasse fehlerhaft.

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:
      element.classList.add('fehlerhaft');
      // 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);

Der zugehörige HTML mit den nötigen IDs könnte so aussehen:

<form action="…" method="post" id="kontaktformular">
  <p>
    <label>
      Ihr Name:
      <input type="text" name="name" id="kontaktformular-name">
    </label>
  </p>
  … weitere Felder …
  <p><button>Absenden</button></p>
</form>

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.

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.

TODO: Beispiel dazu. Mit Nachfahrenselektoren größere Umformatierungen vornehmen, ohne alle Elemente einzeln anzusprechen

Hinzufügen, Löschen und Abfragen von Klassen

Das classList-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:

Direktformatierung über das style-Objekt

Inline-Styles in HTML

Zur direkten CSS-Formatierung eines HTML-Elements existiert das style-Attribut. Es ist eine Zuordnungsliste mit Eigenschaft-Wert-Paaren. Ein HTML-Beispiel:

<p style="color: red; background-color: yellow; font-weight: bold;">Fehler!</p>

Gegenüber dem Einsatz eines Stylesheets sind diese Inline-Styles (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.

Das style-Objekt als Schnittstelle zu Inline-Styles

JavaScript bietet eine Schnittstelle zu diesem style-Attribut: Das style-Objekt bei jedem Elementobjekt. Das style-Objekt hat für jede mögliche CSS-Eigenschaft eine entsprechende Objekteigenschaft. Der CSS-Eigenschaft color entspricht also eine Objekteigenschaft element.style.color vom Typ String.

CSS-Eigenschaftsnamen mit Bindestrichen, wie z.B. background-color, können nicht unverändert als JavaScript-Eigenschaftsnamen übernommen werden. Deshalb werden sie im sogenannten Camel-Case (Groß- und Kleinschreibung im Kamel-Stil) notiert: Der Bindestrich fällt weg, dafür wird der darauf folgende Buchstabe zu einem Großbuchstaben. Aus background-color wird also backgroundColor, aus border-left-width wird borderLeftWidth und so weiter. Die Großbuchstaben in der Wortmitte werden mit Höcker eines Kamels verglichen.

Folgendes Beispiel veranschaulicht das Setzen der Hintergrundfarbe eines Elements auf rot:

document.getElementById('beispielID').style.backgroundColor = 'red';

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:

element.style.marginTop = 15; // Falsch!
element.style.marginTop = '15px'; // Richtig

Besondere JavaScript-Eigenschaftsnamen

Es gibt einen Sonderfall bei der Übersetzung von CSS-Eigenschaftsnamen in JavaScript-Eigenschaftsnamen. Das betrifft die CSS-Eigenschaft float. Dieser Name war einmal ein reserviertes Wort in JavaScript, d.h. er konnte nicht als Bezeichner verwendet werden.

Auch wenn das mittlerweile kein Problem mehr ist, sollten Sie statt float die JavaScript-Eigenschaft cssFloat notieren, also z.B. element.style.cssFloat.

Sinnvoller Einsatz des style-Objekts

Das Setzen von CSS-Formatierungen direkt über das style-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.

Berechnete CSS-Eigenschaften auslesen

Das style-Objekt wird immer wieder missverstanden. Sie können über das style-Objekt nicht den aktuellen, berechneten Wert einer CSS-Eigenschaft auslesen. Sie können damit lediglich Inline-Styles setzen und die bereits gesetzten auslesen.

Die Objekteigenschaften (element.style.eigenschaft) sind allesamt leer, wenn sie per HTML-Inline-Style (style-Attribut) oder wie beschrieben mit JavaScript gesetzt wurden. Folgendes Beispiel verdeutlicht dies:

<p id="ohne-inline-styles">Element ohne Inline-Styles</p>
<p id="mit-inline-styles" style="color: red">Element mit Inline-Styles</p>
// 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
);

Wenn man in Erfahrung bringen will, welche tatsächliche Textfarbe oder welche Pixel-Breite ein Element hat, dann ist nach den sogenannten berechneten Werten (englisch computed values) gefragt.

getComputedStyle

Der Standard, der die Schnittstelle zwischen JavaScript und CSS festlegt, definiert die globale Funktion window.getComputedStyle(). Sie erwartet ein Elementobjekt als ersten Parameter und einen String mit einem CSS-Pseudo-Element als zweiten Parameter (beispielsweise '::after' oder '::before'). Sofern Sie nicht die Formatierung des Pseudo-Elements abfragen wollen, lassen Sie den zweiten Parameter einfach weg.

getComputedStyle() gibt ein Objekt zurück, das genauso aufgebaut ist wie das bereits besprochene style-Objekt. Es enthält für jede CSS-Eigenschaft eine entsprechende Objekteigenschaft mit dem aktuellen berechneten Wert.

var element = document.getElementById('beispielID');
var computedStyle = window.getComputedStyle(element);
window.alert('Textfarbe: ' + computedStyle.color);
window.alert('Elementbreite: ' + computedStyle.width);

Die berechneten Werte (computed values) sind nicht identisch mit den Werten, die Sie im Stylesheet notiert haben. Die Werte in margin-top: 2em; und font-size: 120%; werden in Pixelwerte umgerechnet, sodass getComputedStyle() Werte mit der Einheit px zurückgibt.

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 color: red, so kann es sein, dass getComputedStyle() für color den Wert 'rgb(255, 0, 0)' liefert. Dies ist derselbe Wert in einer anderen Schreibweise.

Elementbox-Größen auslesen

Sie können getComputedStyle() nutzen, um die aktuelle berechnete Höhe (width) und Breite <(height) einer Element-Box auszulesen. Die Methode gibt dann einen String-Wert wie '560px' zurück. Beim Standard-Box-Modell beziehen sich die CSS-Eigenschaft width und height auf die Innengröße. Das heißt Innenabstand (padding), Rahmen (border) und gegebenenfalls Außenrahmen (margin) müssen hinzugefügt werden, um die vollständige Höhe und Breite des Elements zu berechnen.

Es gibt eine Reihe vom Eigenschaften der Elementobjekte, die diese Berechnungen vereinfachen. Im Gegensatz zu getComputedStyle() geben sie keine String-Werte samt Einheiten zurück, sondern direkt JavaScript-Ganzzahlen (Number-Werte) in der Einheit Pixel.

offsetWidth und offsetHeight
liefern die Breite bzw. Höhe der Rahmen-Box des Elements. Das bedeutet, dass der Innenabstand (padding) und der Rahmen (border) inbegriffen sind, der Außenrahmen hingegen (margin) nicht.
clientWidth und clientHeight
liefern Breite bzw. Höhe der Innenabstand-Box des Elements. Das bedeutet, dass padding inbegriffen ist, während border und margin nicht eingerechnet werden. Ebenso wird die Größe einer möglicherweise angezeigte Bildlaufleiste (Scrollbar) nicht einberechnet.
scrollWidth und scrollHeight
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.

In den meisten Fällen werden Sie die äußere Größe eines Elements benötigen, also offsetWidth und offsetHeight. Das folgende Beispiel gibt die Größe eines Elements aus:

var element = document.getElementById('beispielID');
window.alert('Breite: ' + element.offsetWidth + ' Höhe: ' + element.offsetHeight);

getBoundingClientRect

Zugriff auf die eingebundenen Stylesheets

Stylesheets mit JavaScript deaktivieren und einfügen

Stylesheet-Regeln dynamisch erzeugen und ändern