Eine Einführung und Anleitung zum CSS Object Model (CSSOM)

Avatar of Louis Lazaris
Louis Lazaris am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Wenn Sie schon eine Weile JavaScript schreiben, haben Sie mit ziemlicher Sicherheit einige Skripte erstellt, die sich mit dem Document Object Model (DOM) befassen. DOM-Scripting nutzt die Tatsache aus, dass eine Webseite eine Reihe von APIs (oder Schnittstellen) bereitstellt, mit denen Sie Elemente auf einer Seite manipulieren und anderweitig bearbeiten können.

Aber es gibt ein weiteres Objektmodell, mit dem Sie sich vielleicht vertrauter machen möchten: Das CSS Object Model (CSSOM). Wahrscheinlich haben Sie es bereits verwendet, ohne es unbedingt zu wissen.

In dieser Anleitung werde ich viele der wichtigsten Funktionen des CSSOM durchgehen, beginnend mit bekannteren Dingen und dann zu einigen obskureren, aber praktischen Funktionen übergehen.

Was ist das CSSOM?

Laut MDN:

Das CSS Object Model ist eine Reihe von APIs, die die Manipulation von CSS über JavaScript ermöglichen. Es ist dem DOM sehr ähnlich, aber für CSS anstelle von HTML. Es ermöglicht Benutzern, CSS-Stile dynamisch zu lesen und zu ändern.

Die Informationen von MDN basieren auf der offiziellen CSSOM-Spezifikation des W3C. Dieses W3C-Dokument ist eine einigermaßen gute Möglichkeit, sich damit vertraut zu machen, was mit dem CSSOM möglich ist, aber es ist eine absolute Katastrophe für jeden, der praktische Codebeispiele sucht, die die CSSOM-APIs in Aktion umsetzen.

MDN ist viel besser, aber in bestimmten Bereichen immer noch stark unterentwickelt. Daher habe ich in diesem Beitrag versucht, mein Bestes zu tun, um nützliche Codebeispiele und Demos dieser Schnittstellen in Aktion zu erstellen, damit Sie die Möglichkeiten sehen und mit dem Live-Code herumspielen können.

Wie erwähnt, beginnt der Beitrag mit Dingen, die den meisten Frontend-Entwicklern bereits vertraut sind. Diese gängigen Funktionen werden normalerweise mit DOM-Scripting zusammengefasst, sind aber technisch gesehen Teil der größeren Gruppe von Schnittstellen, die über das CSSOM verfügbar sind (obwohl sie auch mit dem DOM überlappen).

Inline-Stile über element.style

Der grundlegendste Weg, CSS-Eigenschaften und -Werte mit JavaScript zu manipulieren oder darauf zuzugreifen, ist über das `style`-Objekt oder die `style`-Eigenschaft, die auf allen HTML-Elementen verfügbar ist. Hier ist ein Beispiel

document.body.style.background = 'lightblue';

Die meisten von Ihnen haben diese Syntax wahrscheinlich schon einmal gesehen oder verwendet. Ich kann die CSS-Formatierung für jedes Objekt auf der Seite mit demselben Format ändern oder hinzufügen: `element.style.propertyName`.

In diesem Beispiel ändere ich den Wert der `background`-Eigenschaft auf `lightblue`. Natürlich ist `background` ein Kurzschrift. Was ist, wenn ich die `background-color`-Eigenschaft ändern möchte? Wandeln Sie für jede mit Bindestrich versehene Eigenschaft den Eigenschaftsnamen in Camel Case um

document.body.style.backgroundColor = 'lightblue';

In den meisten Fällen wird eine einteilige Eigenschaft auf diese Weise durch das einzelne äquivalente Wort in Kleinbuchstaben angesprochen, während Eigenschaften mit Bindestrich in Camel Case dargestellt werden. Die einzige Ausnahme ist die Verwendung der `float`-Eigenschaft. Da `float` ein reserviertes Wort in JavaScript ist, müssen Sie `cssFloat` (oder `styleFloat`, wenn Sie IE8 und früher unterstützen) verwenden. Dies ähnelt dem HTML-Attribut `for`, das als `htmlFor` referenziert wird, wenn etwas wie `getAttribute()` verwendet wird.

Hier ist eine Demo, die die `style`-Eigenschaft verwendet, um dem Benutzer zu ermöglichen, die Hintergrundfarbe der aktuellen Seite zu ändern

Siehe den Pen Using the style Object to Change the Background Color von Louis Lazaris (@impressivewebs) auf CodePen.

Das ist also eine einfache Möglichkeit, eine CSS-Eigenschaft und einen Wert mit JavaScript zu definieren. Aber es gibt eine große Einschränkung bei der Verwendung der `style`-Eigenschaft auf diese Weise: **Dies gilt nur für Inline-Stile des Elements**.

Dies wird deutlich, wenn Sie die `style`-Eigenschaft zum Lesen von CSS verwenden

document.body.style.backgroundColor = 'lightblue';
console.log(document.body.style.backgroundColor);
// "lightblue"

Im obigen Beispiel definiere ich einen Inline-Stil für das `<body>-Element und protokolliere dann denselben Stil in der Konsole. Das ist in Ordnung. Aber wenn ich versuche, eine andere Eigenschaft dieses Elements zu lesen, wird nichts zurückgegeben – es sei denn, ich habe diese Eigenschaft zuvor für dieses Element in meinem CSS oder an anderer Stelle in meinem JavaScript definiert. Zum Beispiel

console.log(document.body.style.color);
// Returns nothing if inline style doesn't exist

Dies würde nichts zurückgeben, selbst wenn ein externes Stylesheet die `color`-Eigenschaft für das `<body>-Element definiert hätte, wie im folgenden CodePen

Siehe den Pen element.style Reads Only Inline Styles von Louis Lazaris (@impressivewebs) auf CodePen.

Die Verwendung von `element.style` ist die einfachste und gebräuchlichste Methode, um Elemente über JavaScript mit Stilen zu versehen. Aber wie Sie sehen können, hat dies eindeutig einige erhebliche Einschränkungen. Lassen Sie uns also einige nützlichere Techniken zum Lesen und Manipulieren von Stilen mit JavaScript betrachten.

Berechnete Stile abrufen

Sie können den berechneten CSS-Wert für jede CSS-Eigenschaft eines Elements mit der Methode `window.getComputedStyle()` lesen

window.getComputedStyle(document.body).background;
// "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box"

Nun, das ist ein interessantes Ergebnis. In gewisser Weise ist `window.getComputedStyle()` der übermäßig wohlwollende Zwilling der `style`-Eigenschaft. Während die `style`-Eigenschaft Ihnen viel zu wenig Informationen über die tatsächlichen Stile eines Elements liefert, kann `window.getComputedStyle()` manchmal zu viel liefern.

Siehe den Pen The getComputedStyle() Method Can Read any CSS Property von Louis Lazaris (@impressivewebs) auf CodePen.

Im obigen Beispiel wurde die `background`-Eigenschaft des `<body>-Elements mit einem einzelnen Wert definiert. Aber die `getComputedStyle()`-Methode gibt alle Werte zurück, die im `background`-Kurzschrift enthalten sind. Diejenigen, die nicht explizit in CSS definiert sind, geben die anfänglichen (oder Standardwerte) für diese Eigenschaften zurück.

Das bedeutet, dass `window.getComputedStyle()` für jede Kurzschrifteigenschaft alle anfänglichen Werte zurückgibt, auch wenn keiner davon in CSS definiert ist

Siehe den Pen window.getComputedStyle() Returns All Longhand Values for a Shorthand Property von Louis Lazaris (@impressivewebs) auf CodePen.

Ähnlich verhält es sich mit Eigenschaften wie `width` und `height`. Sie zeigen die berechneten Abmessungen des Elements an, unabhängig davon, ob diese Werte explizit irgendwo in CSS definiert wurden, wie die folgende interaktive Demo zeigt

Siehe den Pen window.getComputedStyle() Returns Width and Height Values Even if Not Defined in the CSS von Louis Lazaris (@impressivewebs) auf CodePen.

Versuchen Sie, die Größe des Elternelements in der obigen Demo zu ändern, um die Ergebnisse zu sehen. Dies ist einigermaßen vergleichbar mit dem Lesen des Wertes von `window.innerWidth`, abgesehen davon, dass es sich hier um die berechneten CSS-Werte für die angegebene Eigenschaft des angegebenen Elements und nicht nur um eine allgemeine Fenster- oder Viewport-Messung handelt.

Es gibt einige verschiedene Möglichkeiten, auf Eigenschaften mit `window.getComputedStyle()` zuzugreifen. Ich habe bereits eine Möglichkeit gezeigt, die die Punktnotation verwendet, um den kamelkaschierten Eigenschaftsnamen am Ende der Methode hinzuzufügen. Sie können drei verschiedene Möglichkeiten, dies zu tun, im folgenden Code sehen

// dot notation, same as above
window.getComputedStyle(el).backgroundColor;

// square bracket notation
window.getComputedStyle(el)['background-color'];

// using getPropertyValue()
window.getComputedStyle(el).getPropertyValue('background-color');

Die erste Zeile verwendet dasselbe Format wie in der vorherigen Demo. Die zweite Zeile verwendet die Klammernotation, eine gängige JavaScript-Alternative zur Punktnotation. Dieses Format wird nicht empfohlen und Code-Linters warnen davor. Das dritte Beispiel verwendet die Methode `getPropertyValue()`.

Das erste Beispiel erfordert die Verwendung von Camel Case (obwohl in diesem Fall sowohl `float` als auch `cssFloat` funktionieren würden), während die nächsten beiden über dieselbe Syntax auf die Eigenschaft zugreifen wie in CSS verwendet (mit Bindestrichen, oft „Kebab Case“ genannt).

Hier ist dieselbe Demo wie die vorherige, aber diesmal wird `getPropertyValue()` verwendet, um auf die Breiten der beiden Elemente zuzugreifen

Siehe den Pen Using window.getComputedStyle() along with getPropertyValue() von Louis Lazaris (@impressivewebs) auf CodePen.

Berechnete Stile von Pseudo-Elementen abrufen

Ein wenig bekannter Fakt über `window.getComputedStyle()` ist, dass es Ihnen erlaubt, Stilinformationen von Pseudo-Elementen abzurufen. Sie werden oft eine `window.getComputedStyle()`-Deklaration wie diese sehen

window.getComputedStyle(document.body, null).width;

Beachten Sie das zweite Argument, `null`, das der Methode übergeben wird. Firefox vor Version 4 erforderte ein zweites Argument, weshalb Sie es möglicherweise in älterem Code oder von Leuten, die es gewohnt sind, es einzuschließen, sehen. Aber es ist in keinem aktuell verwendeten Browser erforderlich.

Dieser zweite optionale Parameter ermöglicht es mir, anzugeben, dass ich auf die berechneten CSS-Werte eines Pseudo-Elements zugreife. Betrachten Sie das folgende CSS

.box::before {
  content: 'Example';
  display: block;
  width: 50px;
}

Hier füge ich ein `::before`-Pseudo-Element innerhalb des `.box`-Elements hinzu. Mit dem folgenden JavaScript kann ich auf die berechneten Stile dieses Pseudo-Elements zugreifen

let box = document.querySelector('.box');
window.getComputedStyle(box, '::before').width;
// "50px"

Siehe den Pen Using getComputedStyle() to get styles from a pseudo-element von Louis Lazaris (@impressivewebs) auf CodePen.

Sie können dies auch für andere Pseudo-Elemente wie `::first-line` tun, wie im folgenden Code und Demo

let p = document.querySelector('.box p');
window.getComputedStyle(p, '::first-line').color;

Siehe den Pen Using getComputedStyle() to get styles from a pseudo-element von Louis Lazaris (@impressivewebs) auf CodePen.

Und hier ist ein weiteres Beispiel, das das `::placeholder`-Pseudo-Element verwendet, das auf `<input>-Elemente angewendet wird

let input = document.querySelector('input');
window.getComputedStyle(input, '::placeholder').color

Siehe den Pen Using getComputedStyle() to get styles from a ::placeholder pseudo-element von Louis Lazaris (@impressivewebs) auf CodePen.

Das obige funktioniert im neuesten Firefox, aber nicht in Chrome oder Edge (ich habe einen Fehlerbericht für Chrome eingereicht).

Es sollte auch angemerkt werden, dass Browser unterschiedliche Ergebnisse liefern, wenn versucht wird, auf Stile eines nicht existierenden (aber gültigen) Pseudo-Elements zuzugreifen, im Vergleich zu einem Pseudo-Element, das vom Browser überhaupt nicht unterstützt wird (wie ein erfundenes `::banana`-Pseudo-Element). Sie können dies in verschiedenen Browsern mit der folgenden Demo ausprobieren

Siehe den Pen Testing getComputedStyle() on non-existent pseudo-elements von Louis Lazaris (@impressivewebs) auf CodePen.

Nebenbei bemerkt gibt es eine nur in Firefox verfügbare Methode namens `getDefaultComputedStyle()`, die nicht Teil der Spezifikation ist und es wahrscheinlich auch nie sein wird.

Die CSSStyleDeclaration-API

Als ich Ihnen zuvor gezeigt habe, wie man über das `style`-Objekt oder mit `getComputedStyle()` auf Eigenschaften zugreift, haben beide Techniken die `CSSStyleDeclaration`-Schnittstelle offenbart.

Mit anderen Worten, beide folgenden Zeilen geben ein `CSSStyleDeclaration`-Objekt für das `body`-Element des Dokuments zurück

document.body.style;
window.getComputedStyle(document.body);

Im folgenden Screenshot sehen Sie, was die Konsole für jede dieser Zeilen ausgibt

The CSSStyleDeclaration API in the DevTools console

Im Falle von `getComputedStyle()` sind die Werte schreibgeschützt. Im Falle von `element.style` ist das Abrufen und Setzen von Werten möglich, aber wie bereits erwähnt, **wirken sich diese nur auf die Inline-Stile des Dokuments aus**.

setProperty(), getPropertyValue() und item()

Sobald Sie ein `CSSStyleDeclaration`-Objekt auf eine der oben genannten Arten offengelegt haben, haben Sie Zugriff auf eine Reihe nützlicher Methoden zum Lesen oder Manipulieren der Werte. Beachten Sie, dass die Werte im Fall von `getComputedStyle()` schreibgeschützt sind, aber wenn sie über die `style`-Eigenschaft verwendet werden, sind einige Methoden sowohl zum Abrufen als auch zum Setzen verfügbar.

Betrachten Sie den folgenden Code und die Demo

let box = document.querySelector('.box');

box.style.setProperty('color', 'orange');
box.style.setProperty('font-family', 'Georgia, serif');
op.innerHTML = box.style.getPropertyValue('color');
op2.innerHTML = `${box.style.item(0)}, ${box.style.item(1)}`;

Siehe den Pen Using Three Different Methods of the CSSStyleDeclaration API von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Beispiel verwende ich drei verschiedene Methoden des `style`-Objekts

  • Die `setProperty()`-Methode. Diese nimmt zwei Argumente, jeweils einen String: Die Eigenschaft (in normaler CSS-Notation) und den Wert, den Sie der Eigenschaft zuweisen möchten.
  • Die `getPropertyValue()`-Methode. Diese nimmt ein einzelnes Argument entgegen: Die Eigenschaft, deren Wert Sie abrufen möchten. Diese Methode wurde in einem früheren Beispiel mit `getComputedStyle()` verwendet, das, wie erwähnt, ebenfalls ein `CSSStyleDeclaration`-Objekt freilegt.
  • Die `item()`-Methode. Diese nimmt ein einzelnes Argument entgegen, das eine positive Ganzzahl ist, die den Index der Eigenschaft darstellt, auf die Sie zugreifen möchten. Der Rückgabewert ist der Eigenschaftsname an diesem Index.

Beachten Sie, dass in meinem einfachen Beispiel oben nur zwei Stile zur Inline-CSS des Elements hinzugefügt wurden. Das bedeutet, dass der Rückgabewert ein leerer String wäre, wenn ich auf `item(2)` zugreifen würde. Ich würde dasselbe Ergebnis erhalten, wenn ich `getPropertyValue()` verwenden würde, um auf eine Eigenschaft zuzugreifen, die in den Inline-Stilen dieses Elements nicht gesetzt ist.

`removeProperty()` verwenden

Zusätzlich zu den drei oben genannten Methoden gibt es zwei weitere, die auf einem `CSSStyleDeclaration`-Objekt verfügbar sind. Im folgenden Code und Demo verwende ich die `removeProperty()`-Methode

box.style.setProperty('font-size', '1.5em');
box.style.item(0) // "font-size"

document.body.style.removeProperty('font-size');
document.body.style.item(0); // ""

Siehe den Pen Using the removeProperty() method of the CSSSTyleDeclaration API von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Fall, nachdem ich `font-size` mit `setProperty()` gesetzt habe, protokolliere ich den Eigenschaftsnamen, um sicherzustellen, dass er vorhanden ist. Die Demo enthält dann eine Schaltfläche, die beim Anklicken die Eigenschaft mit `removeProperty()` entfernt.

Bei `setProperty()` und `removeProperty()` ist der als Argument übergebene Eigenschaftsname mit Bindestrichen versehen (im selben Format wie in Ihrem Stylesheet) und nicht in Camel Case. Das mag auf den ersten Blick verwirrend erscheinen, aber da der übergebene Wert in diesem Beispiel ein String ist, ist es logisch.

Priorität einer Eigenschaft abrufen und setzen

Schließlich gibt es hier eine interessante Funktion, die ich bei der Recherche für diesen Artikel entdeckt habe: Die Methode `getPropertyPriority()`, demonstriert mit dem folgenden Code und CodePen

box.style.setProperty('font-family', 'Georgia, serif', 'important');
box.style.setProperty('font-size', '1.5em');

box.style.getPropertyPriority('font-family'); // important
op2.innerHTML = box.style.getPropertyPriority('font-size'); // ""

Siehe den Pen Using getPropertyPriority() to get a property’s “importance” von Louis Lazaris (@impressivewebs) auf CodePen.

In der ersten Zeile dieses Codes sehen Sie, dass ich wie zuvor die `setProperty()`-Methode verwende. Beachten Sie jedoch, dass ich ein drittes Argument eingefügt habe. Das dritte Argument ist ein optionaler String, der definiert, ob Sie möchten, dass die Eigenschaft das Schlüsselwort `!important` erhält.

Nachdem ich die Eigenschaft mit `!important` gesetzt habe, verwende ich die `getPropertyPriority()`-Methode, um die Priorität dieser Eigenschaft zu überprüfen. Wenn Sie möchten, dass die Eigenschaft keine Wichtigkeit hat, können Sie das dritte Argument weglassen, das Schlüsselwort `undefined` verwenden oder das dritte Argument als leeren String übergeben.

Und ich sollte hier betonen, dass diese Methoden in Verbindung mit allen Inline-Stilen funktionieren würden, die bereits direkt im HTML im `style`-Attribut eines Elements platziert sind.

Wenn ich also das folgende HTML hätte

<div class="box" style="border: solid 1px red !important;">

Ich könnte jede der in diesem Abschnitt besprochenen Methoden verwenden, um diesen Stil zu lesen oder anderweitig zu manipulieren. Und es sollte hier angemerkt werden, dass, da ich eine Kurzschrifteigenschaft für diesen Inline-Stil verwendet und sie auf `!important` gesetzt habe, alle Langschrift-Eigenschaften, aus denen sich diese Kurzschrift zusammensetzt, beim Verwenden von `getPropertyPriority()` einen Prioritätswert von `important` zurückgeben. Sehen Sie den Code und die Demo unten

// These all return "important"
box.style.getPropertyPriority('border'));
box.style.getPropertyPriority('border-top-width'));
box.style.getPropertyPriority('border-bottom-width'));
box.style.getPropertyPriority('border-color'));
box.style.getPropertyPriority('border-style'));

Siehe den Pen Using getPropertyPriority() to check the priority of longhand properties von Louis Lazaris (@impressivewebs) auf CodePen.

In der Demo, obwohl ich nur die `border`-Eigenschaft explizit im `style`-Attribut gesetzt habe, geben alle zugehörigen Langschrift-Eigenschaften, aus denen sich `border` zusammensetzt, ebenfalls einen Wert von `important` zurück.

Die CSSStyleSheet-Schnittstelle

Bisher befasste sich ein Großteil dessen, was ich betrachtet habe, mit Inline-Stilen (die oft nicht sehr nützlich sind) und berechneten Stilen (die nützlich sind, aber oft zu spezifisch sind).

Eine viel nützlichere API, die es Ihnen ermöglicht, ein Stylesheet abzurufen, das lesbare und beschreibbare Werte hat, und das nicht nur für Inline-Stile, ist die `CSSStyleSheet`-API. Der einfachste Weg, Informationen aus den Stylesheets eines Dokuments abzurufen, ist die `styleSheets`-Eigenschaft des aktuellen Dokuments. Diese legt die `CSSStyleSheet`-Schnittstelle frei.

Zum Beispiel zählt die folgende Zeile die `length`-Eigenschaft, um zu sehen, wie viele Stylesheets das aktuelle Dokument hat

document.styleSheets.length; // 1

Ich kann auf jedes Stylesheet des Dokuments mit Null-basierter Indizierung zugreifen

document.styleSheets[0];

Wenn ich dieses Stylesheet in meine Konsole protokolliere, kann ich die verfügbaren Methoden und Eigenschaften anzeigen

The CSSStyleSheet Interface in the DevTools Console

Die `cssRules`-Eigenschaft wird sich als nützlich erweisen. Diese Eigenschaft liefert eine Liste aller CSS-Regeln (einschließlich Deklarationsblöcken, At-Regeln, Media-Regeln usw.), die in diesem Stylesheet enthalten sind. In den folgenden Abschnitten werde ich detailliert beschreiben, wie diese API verwendet wird, um Stile aus einem externen Stylesheet zu manipulieren und zu lesen.

Mit einem Stylesheet-Objekt arbeiten

Zur Vereinfachung arbeiten wir mit einem Beispiel-Stylesheet, das nur eine Handvoll Regeln enthält. Dies ermöglicht es mir, zu demonstrieren, wie das CSSOM verwendet wird, um auf die verschiedenen Teile eines Stylesheets zuzugreifen, ähnlich wie beim Zugriff auf Elemente über DOM-Scripting.

Hier ist das Stylesheet, mit dem ich arbeiten werde

* {
  box-sizing: border-box;
}

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 2em;
  line-height: 1.4;
}

main {
  width: 1024px;
  margin: 0 auto !important;
}

.component {
  float: right;
  border-left: solid 1px #444;
  margin-left: 20px;
}

@media (max-width: 800px) {
  body {
    line-height: 1.2;
  }

  .component {
    float: none;
    margin: 0;
  }
}

a:hover {
  color: lightgreen;
}

@keyframes exampleAnimation {
  from {
    color: blue;
  }
  
  20% {
    color: orange;
  }
  
  to {
    color: green;
  }
}

code {
  color: firebrick;
}

Es gibt eine Reihe von Dingen, die ich mit diesem Beispiel-Stylesheet versuchen kann, und ich werde hier einige davon demonstrieren. Zuerst werde ich alle Stilregeln im Stylesheet durchlaufen und den Selektortext für jede einzelne protokollieren

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('p');

for (i of myRules) {
  if (i.type === 1) {
    p.innerHTML += `<c​ode>${i.selectorText}</c​ode><br>`;
  }
}

Siehe den Pen Working with CSSStyleSheet – Logging the Selector Text von Louis Lazaris (@impressivewebs) auf CodePen.

Ein paar Dinge, die im obigen Code und der Demo zu beachten sind. Erstens speichere ich eine Referenz auf das `cssRules`-Objekt meines Stylesheets zwischen. Dann durchlaufe ich alle Regeln in diesem Objekt und prüfe, welchen Typ jede einzelne hat.

In diesem Fall möchte ich Regeln vom Typ `1`, was der Konstante `STYLE_RULE` entspricht. Andere Konstanten sind `IMPORT_RULE` (3), `MEDIA_RULE` (4), `KEYFRAMES_RULE` (7) usw. Sie können eine vollständige Tabelle dieser Konstanten in diesem MDN-Artikel einsehen.

Wenn ich bestätige, dass eine Regel eine Stilregel ist, gebe ich für jede dieser Stilregeln die Eigenschaft `selectorText` aus. Dies ergibt für das angegebene Stylesheet die folgenden Zeilen

*
body
main
.component
a:hover
code

Die `selectorText`-Eigenschaft ist eine String-Darstellung des in dieser Regel verwendeten Selektors. Dies ist eine beschreibbare Eigenschaft, sodass ich, wenn ich möchte, den Selektor für eine bestimmte Regel in meiner ursprünglichen `for`-Schleife mit dem folgenden Code ändern kann

if (i.selectorText === 'a:hover') {
  i.selectorText = 'a:hover, a:active';
}

Siehe den Pen Working with the CSSStyleSheet API – Changing the Selector Text von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Beispiel suche ich nach einem Selektor, der `:hover`-Stile für meine Links definiert, und erweitere den Selektor, um dieselben Stile auf Elemente im `:active`-Zustand anzuwenden. Alternativ könnte ich eine Art String-Methode oder sogar ein reguläres Ausdrucks-Tool verwenden, um nach allen Vorkommen von `:hover` zu suchen und dann etwas von dort aus zu tun. Aber das sollte genügen, um zu demonstrieren, wie es funktioniert.

Zugriff auf @media-Regeln mit dem CSSOM

Sie werden bemerken, dass mein Stylesheet auch eine Media Query-Regel und einen Keyframe-At-Rule-Block enthält. Beide wurden übersprungen, als ich nach Stilregeln (Typ 1) gesucht habe. Lassen Sie uns nun alle `@media`-Regeln finden

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('.output');

for (i of myRules) {
  if (i.type === 4) {
    for (j of i.cssRules) {
      p.innerHTML += `<c​ode>${j.selectorText}</c​ode><br>`;
    }
  }
}

Basierend auf dem gegebenen Stylesheet ergibt das obige

body
.component

Siehe den Pen Working with the CSSStyleSheet API – Accessing @media Rules von Louis Lazaris (@impressivewebs) auf CodePen.

Wie Sie sehen können, durchlaufe ich, nachdem ich alle Regeln durchgegangen bin, um zu sehen, ob `@media`-Regeln vorhanden sind (Typ 4), dann das `cssRules`-Objekt für jede Medienregel (in diesem Fall gibt es nur eine) und protokolliere den Selektortext für jede Regel innerhalb dieser Medienregel.

Die für eine `@media`-Regel bereitgestellte Schnittstelle ähnelt der für ein Stylesheet bereitgestellten Schnittstelle. Die `@media`-Regel enthält jedoch auch eine `conditionText`-Eigenschaft, wie im folgenden Snippet und der Demo gezeigt

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('.output');

for (i of myRules) {
  if (i.type === 4) {
    p.innerHTML += `<c​ode>${i.conditionText}</c​ode><br>`;
    // (max-width: 800px) 
  }
}

Siehe den Pen Working with the CSSStyleSheet API – Accessing @media Rules von Louis Lazaris (@impressivewebs) auf CodePen.

Dieser Code durchläuft alle Media-Query-Regeln und protokolliert den Text, der bestimmt, wann diese Regel anwendbar ist (d. h. die Bedingung). Es gibt auch eine `mediaText`-Eigenschaft, die denselben Wert zurückgibt. Laut Spezifikation können Sie beides abrufen oder setzen.

Zugriff auf @keyframes-Regeln mit dem CSSOM

Nachdem ich gezeigt habe, wie Informationen aus einer `@media`-Regel gelesen werden, betrachten wir nun, wie auf eine `@keyframes`-Regel zugegriffen wird. Hier ist ein Code zum Einstieg

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('.output');

for (i of myRules) {
  if (i.type === 7) {
    for (j of i.cssRules) {
     p.innerHTML += `<c​ode>${j.keyText}</c​ode><br>`;
    }
  }
}

Siehe den Pen Working with the CSSStyleSheet API – Accessing @keyframes Rules von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Beispiel suche ich nach Regeln mit dem Typ 7 (d. h. `@keyframes`-Regeln). Wenn eine gefunden wird, durchlaufe ich alle `cssRules` dieser Regel und protokolliere die Eigenschaft `keyText` für jede. Die Protokollierung wird in diesem Fall lauten

"0%"
"20%"
"100%"

Sie werden bemerken, dass mein ursprüngliches CSS `from` und `to` als erste und letzte Keyframes verwendet, aber die `keyText`-Eigenschaft berechnet diese zu `0%` und `100%`. Der Wert von `keyText` kann auch gesetzt werden. In meinem Beispiel-Stylesheet könnte ich ihn so hart kodieren

// Read the current value (0%)
document.styleSheets[0].cssRules[6].cssRules[0].keyText;

// Change the value to 10%
document.styleSheets[0].cssRules[6].cssRules[0].keyText = '10%'

// Read the new value (10%)
document.styleSheets[0].cssRules[6].cssRules[0].keyText;

Siehe den Pen Working with the CSSStyleSheet API – Setting @keyframes Rules von Louis Lazaris (@impressivewebs) auf CodePen.

Damit können wir Keyframes einer Animation im Fluss einer Webanwendung oder möglicherweise als Reaktion auf eine Benutzeraktion dynamisch ändern.

Eine weitere Eigenschaft, die beim Zugriff auf eine @keyframes-Regel verfügbar ist, ist name.

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('.output');

for (i of myRules) {
  if (i.type === 7) {
    p.innerHTML += `<c​ode>${i.name}</c​ode><br>`;
  }
}

Siehe den Pen Arbeiten mit der CSSStyleSheet API – Den Namen einer @keyframes-Regel abrufen von Louis Lazaris (@impressivewebs) auf CodePen.

Erinnern Sie sich, dass die @keyframes-Regel in CSS so aussieht:

@keyframes exampleAnimation {
  from {
    color: blue;
  }
  
  20% {
    color: orange;
  }
  
  to {
    color: green;
  }
}

Somit ermöglicht mir die Eigenschaft name, den benutzerdefinierten Namen zu lesen, der für diese @keyframes-Regel gewählt wurde. Dies ist derselbe Name, der in der Eigenschaft animation-name verwendet würde, wenn die Animation auf ein bestimmtes Element angewendet wird.

Eine letzte Sache, die ich hier erwähnen werde, ist die Möglichkeit, spezifische Stile innerhalb eines einzelnen Keyframes abzurufen. Hier ist ein Beispielcode mit einer Demo:

let myRules = document.styleSheets[0].cssRules,
    p = document.querySelector('.output');

for (i of myRules) {
  if (i.type === 7) {
    for (j of i.cssRules) {
      p.innerHTML += `<c​ode>${j.style.color}</c​ode><br>`;
    }
  }
}

Siehe den Pen Arbeiten mit der CSSStyleSheet API – Zugreifen auf Eigenschaftswerte innerhalb von @keyframes-Regeln von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Beispiel durchlaufe ich nach dem Auffinden der @keyframes-Regel jede der Regeln im Keyframe (z. B. die "from"-Regel, die "20%"-Regel usw.). Dann greife ich innerhalb jeder dieser Regeln auf eine einzelne style-Eigenschaft zu. In diesem Fall, da ich weiß, dass color die einzige definierte Eigenschaft für jede ist, protokolliere ich lediglich die Farbwerte.

Die wichtigste Erkenntnis in diesem Fall ist die Verwendung der style-Eigenschaft oder des Objekts. Zuvor habe ich gezeigt, wie diese Eigenschaft zum Zugreifen auf Inline-Stile verwendet werden kann. Aber in diesem Fall verwende ich sie, um auf die einzelnen Eigenschaften innerhalb eines einzelnen Keyframes zuzugreifen.

Sie können wahrscheinlich sehen, wie dies einige Möglichkeiten eröffnet. Dies ermöglicht es Ihnen, die Eigenschaften eines einzelnen Keyframes im Handumdrehen zu ändern, was als Ergebnis einer Benutzeraktion oder etwas anderem, das in einer App oder möglicherweise einem webbasierten Spiel stattfindet, geschehen könnte.

Hinzufügen und Entfernen von CSS-Deklarationen

Die Schnittstelle CSSStyleSheet bietet Zugriff auf zwei Methoden, mit denen Sie eine gesamte Regel aus einem Stylesheet hinzufügen oder entfernen können. Die Methoden sind: insertRule() und deleteRule(). Sehen wir uns beide in Aktion an, wie sie unser Beispiel-Stylesheet bearbeiten:

let myStylesheet = document.styleSheets[0];
console.log(myStylesheet.cssRules.length); // 8

document.styleSheets[0].insertRule('article { line-height: 1.5; font-size: 1.5em; }', myStylesheet.cssRules.length);
console.log(document.styleSheets[0].cssRules.length); // 9

Siehe den Pen Arbeiten mit der CSSStyleSheet API – Regeln einfügen von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Fall protokolliere ich die Länge der Eigenschaft cssRules (was zeigt, dass das Stylesheet ursprünglich 8 Regeln enthält), dann füge ich das folgende CSS als einzelne Regel mit der Methode insertRule() hinzu:

article {
  line-height: 1.5;
  font-size: 1.5em;
}

Ich protokolliere die Länge der Eigenschaft cssRules erneut, um zu bestätigen, dass die Regel hinzugefügt wurde.

Die Methode insertRule() nimmt einen String als ersten Parameter (der obligatorisch ist) entgegen, der die vollständige Stilregel enthält, die Sie einfügen möchten (einschließlich Selektor, geschweifte Klammern usw.). Wenn Sie eine At-Regel einfügen, können Sie die vollständige At-Regel, einschließlich der einzelnen verschachtelten Regeln innerhalb der At-Regel, in diesem String angeben.

Das zweite Argument ist optional. Dies ist eine Ganzzahl, die die Position oder den Index darstellt, an dem Sie die Regel einfügen möchten. Wenn dies nicht angegeben wird, ist der Standardwert 0 (was bedeutet, dass die Regel am Anfang der Regelsammlung eingefügt wird). Wenn der Index größer als die Länge des Regelobjekts ist, wird ein Fehler ausgelöst.

Die Methode deleteRule() ist viel einfacher zu verwenden:

let myStylesheet = document.styleSheets[0];
console.log(myStylesheet.cssRules.length); // 8

myStylesheet.deleteRule(3);
console.log(myStylesheet.cssRules.length); // 7

Siehe den Pen Arbeiten mit der CSSStyleSheet API – Regeln löschen von Louis Lazaris (@impressivewebs) auf CodePen.

In diesem Fall akzeptiert die Methode ein einzelnes Argument, das den Index der Regel darstellt, die ich entfernen möchte.

Bei beiden Methoden muss der als Argument übergebene ausgewählte Index aufgrund der nullbasierten Indizierung kleiner als die Länge des cssRules-Objekts sein, andernfalls wird ein Fehler ausgelöst.

Die CSSStyleDeclaration API erneut betrachten

Zuvor habe ich erklärt, wie auf einzelne Eigenschaften und Werte zugegriffen werden kann, die als Inline-Stile deklariert sind. Dies geschah über element.style, was die Schnittstelle CSSStyleDeclaration exponierte.

Die CSSStyleDeclaration API kann jedoch auch auf einer einzelnen Stilregel als Teilmenge der CSSStyleSheet API exponiert werden. Dies habe ich bereits angedeutet, als ich Ihnen gezeigt habe, wie Sie auf Eigenschaften in einer @keyframes-Regel zugreifen können. Um zu verstehen, wie dies funktioniert, vergleichen Sie die folgenden beiden Code-Snippets:

<div style="color: lightblue; width: 100px; font-size: 1.3em !important;"></div>
.box {
  color: lightblue;
  width: 100px;
  font-size: 1.3em !important;
}

Das erste Beispiel sind Inline-Stile, auf die wie folgt zugegriffen werden kann:

document.querySelector('div').style

Dies exponiert die CSSStyleDeclaration API, die es mir ermöglicht, Dinge wie element.style.color, element.style.width usw. zu tun.

Aber ich kann die exakt gleiche API auf einer einzelnen Stilregel in einem externen Stylesheet exponieren. Das bedeutet, dass ich meine Verwendung der style-Eigenschaft mit der CSSStyleSheet-Schnittstelle kombiniere.

Der CSS-Code im zweiten obigen Beispiel, der dieselben Stile wie die Inline-Version verwendet, kann also wie folgt aufgerufen werden:

document.styleSheets[0].cssRules[0].style

Dies öffnet ein einzelnes CSSStyleDeclaration-Objekt für die eine Stilregel im Stylesheet. Wenn es mehrere Stilregeln gäbe, könnte jede über cssRules[1], cssRules[2], cssRules[3] und so weiter aufgerufen werden.

Innerhalb eines externen Stylesheets, in einer einzelnen Stilregel vom Typ 1, habe ich Zugriff auf alle zuvor erwähnten Methoden und Eigenschaften. Dazu gehören setProperty(), getPropertyValue(), item(), removeProperty() und getPropertyPriority(). Darüber hinaus sind dieselben Funktionen für eine einzelne Stilregel innerhalb einer @keyframes- oder @media-Regel verfügbar.

Hier ist ein Code-Snippet und eine Demo, die zeigen, wie diese Methoden auf einer einzelnen Stilregel in unserem Beispiel-Stylesheet verwendet werden würden:

// Grab the style rules for the body and main elements
let myBodyRule = document.styleSheets[0].cssRules[1].style,
    myMainRule = document.styleSheets[0].cssRules[2].style;

// Set the bg color on the body
myBodyRule.setProperty('background-color', 'peachpuff');

// Get the font size of the body
myBodyRule.getPropertyValue('font-size');

// Get the 5th item in the body's style rule
myBodyRule.item(5);

// Log the current length of the body style rule (8)
myBodyRule.length;

// Remove the line height
myBodyRule.removeProperty('line-height');

// log the length again (7)
myBodyRule.length;

// Check priority of font-family (empty string)
myBodyRule.getPropertyPriority('font-family');

// Check priority of margin in the "main" style rule (!important)
myMainRule.getPropertyPriority('margin');

Siehe den Pen Arbeiten mit dem Style-Objekt einer einzelnen Stilregel in einem externen Stylesheet von Louis Lazaris (@impressivewebs) auf CodePen.

Das CSS Typed Object Model… Die Zukunft?

Nach allem, was ich in diesem Artikel betrachtet habe, wäre es seltsam, wenn ich die Nachricht überbringen müsste, dass es möglich ist, dass das CSSOM, wie wir es kennen, eines Tages größtenteils obsolet sein wird.

Das liegt an etwas namens CSS Typed OM, das Teil von dem Houdini-Projekt ist. Obwohl einige Leute angemerkt haben, dass das neue Typed OM im Vergleich zum aktuellen CSSOM umständlicher ist, beinhalten die Vorteile, wie in diesem Artikel von Eric Bidelman dargelegt,:

  • Weniger Fehler
  • Arithmetische Operationen und Einheitenumrechnung
  • Bessere Leistung
  • Fehlerbehandlung
  • CSS-Eigenschaftsnamen sind immer Strings

Vollständige Details zu diesen Funktionen und einen Einblick in die Syntax finden Sie im vollständigen Artikel.

Zum Zeitpunkt des Schreibens wird CSS Typed OM nur in Chrome unterstützt. Den Fortschritt der Browserunterstützung können Sie in diesem Dokument einsehen.

Abschließende Worte

Das Bearbeiten von Stylesheets über JavaScript ist sicherlich nichts, was Sie in jedem Projekt tun werden. Und einige der komplexen Interaktionen, die durch die von mir eingeführten Methoden und Eigenschaften ermöglicht werden, haben sehr spezifische Anwendungsfälle.

Wenn Sie eine Art von Tool erstellt haben, das eine dieser APIs verwendet, würde ich gerne davon hören. Meine Recherchen haben nur an der Oberfläche dessen gekratzt, was möglich ist, aber ich würde gerne sehen, wie dies in realen Beispielen verwendet werden kann.

Ich habe alle Demos aus diesem Artikel in einer CodePen-Sammlung zusammengefasst, damit Sie gerne damit herumspielen können.