Jan 10
CSS-Stylesheets zur Laufzeit mit Javascript erzeugen
An dieser Stelle möchte ich eine relative unbekannte Möglichkeit zur Manipulation des Aussehens von Webseiten vorstellen, über die ich selbst erst vor wenigen Tagen gestolpert bin. Die Rede ist von DOM CSS.
In den meisten Anwendungsfällen ändert man das Erscheinungsbild von Elementen, indem man diese direkt anspricht (zum Beispiel mit getElementById(id) ) und mittels style-Attribut beeinflusst.
Mit DOM CSS ist es möglich, solche Änderungen in CSS-Regeln wie div.menu { color: black } durchzuführen. Dazu bieten alle modernen Browser Funktionen zum Entfernen bestehender und zum Erstellen neuer CSS-Regeln.
Dabei halten sich die meisten Browser-Entwickler an die W3C-Vorgaben, nur Microsofts Internet Explorer – wie so oft – nicht. Dennoch bietet der IE ähnliche Funktionen.
Grundsätzlich adressiert man zuerst das Stylesheet, das man mit Javascript beeinflussen will. Dies kann man zum Beispiel über die getElement-Methoden tun.
Das document-Object stellt mit document.styleSheets[index] eine weitere Möglichkeit zum Ansprechen von Stylesheets zur Verfügung, womit sowohl alle internen style-Elemente als auch alle externen, mit link erzeugten CSS-Definitionen erreicht werden können.
In standardkonformen Browsern greift man mit cssRules[index] auf bestehende CSS-Regeln zu, der IE reagiert nur auf die MS-eigene Methode rules[index].
Anschließend kann man mit dem style-Attribut wie gewohnt die Eigenschaftswerte verändern. Zum Beispiel:
document.styleSheets[0].cssRules[0].style.backgroundColor = "yellow";
Außerdem gibt es die Möglichkeit mittels der selectorText-Methode den Selector eines CSS-Regel auszulesen. Damit lässt eine Schleife konstruieren, die alle CSS-Regeln nach einem bestimmten Eintrag durchsucht, wie nachfolgend demonstriert:
if( document.styleSheets[0].cssRules ) { var myrules = document.styleSheets[0].cssRules; } else { if ( document.styleSheets[0].rules ) { var myrules = document.styleSheets[0].rules; } } if( myrules ) { for( i = 0; myrules[i]; i++ ){ if( myrules[i].selectorText == "p"){ myrule = myrules[i]; break; } } }
Um neue Regeln hinzuzufügen, verwendet man in standardkonformen Browsern die insertRule[definition, index]-Methode, für den Explorer heißt die Funktion addRule[selector, style, index (optional)].
Vorhandene CSS-Definitionen löscht man mittels deleteRule[index] beziehungsweise removeRule[index (optional)] im IE.
Nachfolgend ist der komplette Code eines Mini-Beispiel zu finden. Dabei wird zuerst ein neues style-Elemente zur Laufzeit erstellt, in das HTML-Gerüst eingebettet und abschließend mittels DOM CSS eine Regel für das body-Element festgelegt, die Hintergrund- und Schriftfarbe verändert.
<br /> if( document.styleSheets ) { var myStyle = document.createElement("style"); myStyle.setAttribute( "type", "text/css" ); document.getElementsByTagName("head")[0].appendChild(myStyle); var styles = document.styleSheets.length; myStyle = document.styleSheets[styles-1]; if( document.styleSheets[0].cssRules ) { myStyle.insertRule("body { color: red; background-color: black; }", 0); } else { if ( document.styleSheets[0].rules ) { myStyle.addRule("body", "color: red; background-color: black;"); } } }
Ich favorisiere den Weg über das Hinzufügen eines neuen style-Elements anstatt dem Bearbeiten vorhandener Definitionen, da mir dies übersichtlicher und logischer vorkommt. Zumal – in den meisten Fällen – die zuletzt festgelegte CSS-Regel bei Überschneidungen zur Darstellung vom Browser genutzt wird.
Der Vorteil gegenüber dem traditionellen Ändern von style-Attributen über das direkte Ansprechen liegt vor allem in der Geschwindigkeit bei einer großen Anzahl von Elementen, die verändert werden sollen.
Ein Browser kann erheblich schneller 1.000 Elementen eine neue Schriftfarbe über eine CSS-Regel zuweisen als in einer Schleife eben diese Elemente direkt zu adressieren und jedem die neue Schriftfarbe zuzuweisen.
Interessant ist DOM CSS daher bei Javascript-Anwendungen, die neue HTML-Elemente (für runde Ecken beispielsweise) in den Code einbinden.
Januar 16th, 2009 at 12:56 pm
Danke für die gute Erklärung!
Wie rückwärtskompatibel ist das denn?
Januar 16th, 2009 at 2:28 pm
Auf jeden Fall funktioniert es mit dem Internet Explorer ab Version 6. Firefox 3 – sicherlich auch 2 – kommt damit ebenso zurecht wie Opera 9+ und Safari 3. Safari 2 kann wegen nicht-vorhandenseins eines (älteren) Macs nicht testen.
April 16th, 2009 at 8:39 am
Prinzipiell funktioniert es in allen Browsern mit „modernem“ DOM. Nur Opera besitzt das styleSheets-Objekt erst ab Version 9. Und dann gibt es noch das Problem, daß der IE/Mac sowohl die (proprietäre) rules- als auch die (standardisierte) cssRules-Collection kennt. Leider hat er aber bei cssRules nicht die Methoden/Eigenschaften, die ein standardkonformer Browser hier hat. Beim Konqueror (mindestens bis 3.x) ist es genau andersherum. Er kennt ebenfalls beide Collections, aber er beherrscht nur die standardkonformen Methoden/Eigenschaften. Außerdem haben (mindestens ältere) Browser mit KHTML-Engine (also im wesentlichen Konqueror & Safari) einen Bug bei insertRule(), der die Nutzung verhindert.
Ich gehe deswegen einen anderen Weg, um neue Regeln anzuhängen. Die IEs bekommen ihren proprietären Weg, und der standardkonforme Rest bekommt einen Workaround, der auch auf älteren Operas, Safaris und Konquerors funktioniert: Das interne Stylesheet nicht aus Sicht des CSSDOMs sondern aus Sicht des (HTML-)DOMs behandeln. Also als simpler Textknoten, und diesem Textknoten einfach neuen „Stylesheet-Text“ hinzufügen (s. addStyle()).
Oktober 8th, 2014 at 7:09 am
[…] zweiten Spiegelstrich das dynamische Erzeugen einer Klasse gemeint ist findet sich hier ein Tut: CSS-Stylesheets zur Laufzeit mit Javascript erzeugen | Ajaxschmiede.de Ansonsten musst du sagen, was du mit "Selector" meinst. Den Begriff verwende ich nur, […]