Donnerstag, 16. August 2012

Dynamisch StyleSheet Regeln schreiben

Um CSS Eigenschaften von DOM Elementen mit JavaScript nachträglich zu ändern, gibt es unterschiedliche Möglichkeiten. Zum einen können die Eigenschaften über das Style Attribut direkt adressiert und manipuliert werden, wodurch inline Styles geschrieben werden. Zum anderen können CSS Klassen hinzugefügt oder entfernt werden, wodurch die im StyleSheet definierten Regeln angewendet werden. Eine weitere Alternative ist das Editieren eines StyleSheet Dokuments mit JavaScript.

element.style


In vielen Fällen ist das direkte Anpassen einer CSS Eigenschaft eines Elements die offensichtlichste und einfachste Lösung. Dazu muss das Element lediglich selektiert werden und anschließend können die anzupassenden Eigenschaften an dessen Style Attribut gesetzt werden.
// native JavaScript
var elm = document.getElementById("elm");
elm.style.width = "100%";
// jQuery
var elm = $("#elm");
elm.css("width", "100%");
Die dadurch erzielten Änderungen finden sich direkt im DOM wieder.
<!-- before -->
<div id ="elm"></div>
<!-- after -->
<div id="elm" style="width:100%;"></div>
Mit jQuery können auch gleichzeitig mehre CSS Eigenschaften geändert werden.
elm.css({width: "100%", height: "auto"});
Werden immer nur einzelne Elemente auf diese Weise angepasst, ist dies eine bequeme und effektive Lösung. Sollen jedoch eine Vielzahl an Elementen angepasst werden, würden auch dementsprechend häufig DOM-Manipulationen durchgeführt, was verhältnismäßig langsam ist und sich negativ auf den Speicherbedarf auswirkt.

element.className


Durch Zuweisen und Entfernen von CSS Klassen an einem Element können auch mehrere vorher festgelegte Eigenschaften aus dem StyleSheet auf ein Element angewandt werden. Dies dient zum Beispiel für unterschiedlich definierte Zustände.
// native JavaScript
var elm = document.getElementById("elm");
elm.className += " active";
// jQuery
var elm = $("#elm");
elm.addClass("active");
Die dadurch erzielten Änderungen finden sich nur indirekt im DOM wieder. Der hinzugefügte Klassenname lässt eine Anpassung der CSS Eigenschaften vermuten, die konkreten Werte sind jedoch nur im StyleSheet selbst hinterlegt.
<!-- before -->
<div id="elm"></div>
<!-- after -->
<div id="elm" class=" active"></div>
Das Entfernen einer Klasse ist etwas aufwendiger, da andere bereits vorhandene Klassen nicht überschrieben werden sollen.
// native JavaScript
elm.className = elm.className.replace(/\bactive\b/, "");
// jQuery
elm.removeClass("active");
Dadurch können die DOM-Manipulationen minimiert werden, da die eine Klasse an einem Elternelement geschrieben werden kann und sich Regeln für dessen gesamte Kindelemente auswirken können. Ein Nachteil dieser Methode ist, dass alle Klassen bereits vorher im StyleSheet definiert sein müssen, wodurch damit keine dynamisch zu berechnenden Werte hinzufügen lassen. Für vordefinierte Zustände hingegen ist dies eine gute Lösung. So lassen sich zum Beispiel Elemente einer Navigation mit einer Klasse hervorheben.

stylesheet.insertRule


Eine weitere Möglichkeit ist das dynamische Schreiben von StyleSheet Regeln. Dadurch wird zum einen das DOM nicht manipuliert und zum anderen können auch mehrere Elemente gleichzeitig angepasst werden.

Um die geschriebenen StyleSheet Regeln auch wieder einfach löschen zu können, wird empfohlen eigenes leeres StyleSheet Dokument zu nutzen. Nicht mehr benötigte Regeln sollten nicht im StyleSheet Dokument bleiben und nur durch die Cascade überschrieben, sondern tatsächlich wieder entfernt werden. Ansonsten benötigt der Browser immer mehr Zeit bei jedem weiteren Rendervorgang zum Auswerten der Gewichtigkeit der erzeugten Regeln.
<!-- somewhere in the head section -->
<style type="text/css" id="sheet"></style>
In diesem Beispiel wird ein Style Tag und damit ein leeres StyleSheet Dokument direkt im Markup definiert. Theoretisch könnte dieses auch dynamisch mit JavaScript erzeugt und anschließen dem DOM hinzufügen werden, aber gerade die DOM Interaktion soll ja reduziert werden.
var sheet = document.getElementById("sheet").sheet || document.styleSheets.sheet;
var selector = "#elm";
var rulestr = "width:100%;height:auto;";
if (sheet.insertRule) {
  sheet.cssRules.length && sheet.deleteRule(0);
  sheet.insertRule(selector + "{" + rulestr + "}", 0);
} else {
  sheet.cssRules && sheet.cssRules.length && sheet.removeRule();
  sheet.addRule(selector, rulestr);
}
Auf das StyleSheet Dokument kann mithilfe dessen DOM ID zugegriffen werden. Leider ist die Syntax zum Editieren von Regeln nicht in allen Browsern einheitlich. Mit insertRule bzw. addRule können Regeln hinzufügt und mit deleteRule bzw. removeRule auch wieder entfernt werden. An welche Stelle die Regel hinzugefügt oder welche Regel gelöscht werden soll, kann mithilfe des Indexes angegeben werden. Der Selektor kann den gleichen komplexen Aufbau wie eine übliche CSS Regel haben. Mit der Ausnahme, dass im IE nur ein Selektor und nicht mehrere durch Kommas getrennte Selektoren in einer neuen Regel verwendet werden darf.

Anwendungsbeispiel


Ein Beispiel für das Schreiben dynamischer Styleregeln ist hier hinterlegt. Das Beispiel zeigt fünf nebeneinander angeordnete Bilder, welche immer auf die maximale Größe skaliert werden, ohne dabei deren Seitenverhältnis zu zerstören. Dazu müssen die Bilder entweder in der Breite oder in der Höhe aufgezogen werden. Zusätzlich werden sie zentriert, um den Bildmittelpunkt zu bewahren.

In dieser Form ist dies nicht über CSS Klassen abzubilden, da die Werte von der Browsergröße abhängig sind und somit nicht vorher definiert werden können, sondern berechnet werden müssen. Würde dieses Verhalten mit Style Attributen realisiert werden, müssten bei einer Änderung der Browsergröße die Styles jedes einzelnen Bildes immer wieder neu geschrieben werden, wodurch sehr viele DOM-Manipulationen nötig wären.

Stattdessen ist es viel performanter, die Regel für die Skalierung und Zentrierung aller Bilder dynamisch in ein StyleSheet Dokument zu schreiben. Dadurch wird das DOM nicht manipuliert und es kann sichergestellt werden, dass der Browser auch nur einen Repaint für das Skalieren aller Bilder vornehmen muss.

Keine Kommentare:

Kommentar veröffentlichen