Grundlegendes
CSS Spezifität berechnen: Selektoren gewichten, Kaskade verstehen – mit Beispielen
CSS Spezifität entscheidet, wenn zwei Regeln dasselbe Element treffen und dieselbe Eigenschaft setzen. Sie ist kein Punktesystem, sondern ein Tupel aus vier Zahlen. Wie man es berechnet, wo es in der Kaskade steht und woran die üblichen Rechenfehler liegen.
Erläuterungen
Die Spezifizität 1) ist eine der Kriterien, nach denen ein Anwenderprogramm im Konfliktfall die vorhandenen CSS-Regeln sortiert.
Um die Spezifizität eines Selektors zu ermitteln, teilt man dessen Einfach- Selektoren nach den folgenden vier Gruppen ein:
- Gruppe a:
- (diese Gruppe ist neu in CSS 2.1) 2)
- wird auf 1 gesetzt für CSS-Regeln, die durch ein Attribut
styleim Quelltext definiert sind und ist in allen anderen Fällen 0. Das HTML-Attribut ersetzt hier den Selektor. - Gruppe b:
- Dies ist die Anzahl der ID-Attribute des Selektors.
- Gruppe c:
- Entspricht der Anzahl aller anderen Attribute (einschließlich Klassen) und Pseudoklassen innerhalb des Selektors.
- Gruppe d:
- Dies ist die Anzahl der Elementnamen und Pseudoelemente, die der Selektor beinhaltet.
Danach stellt man die Anzahl der Selektoren, die zu jeder Gruppe gehören, als eine Kette von vier Ziffern in der Form a-b-c-d hintereinander.
Werte in den linken Spalten und höhere Zahlenwerte bedeuten dabei höhere Spezifizität:
- 0-0-1-2 ist höher als 0-0-0-12
- 0-1-0-1 ist höher als 0-0-1-1
- 0-1-0-2 ist höher als 0-1-0-1.
Diese Ordnung ist etwa vergleichbar der Ordnung der Medaillenränge bei Sportveranstaltungen. Per style im Quelltext definierte Regeln haben immer die Spezifizität 1-0-0-0, weil hier keine Selektoren existieren können.
Elemente und Attribute im Quelltext, die laut HTML 4.01 als 'missbilligt' (= deprecated) eingestuft sind, z. B. font color, hspace, align usw., müssen vom Browser zuerst in ihre CSS-Entsprechung umgewandelt werden. Diese erhält dann die Spezifizität 0-0-0-0.
Beispiele
(Die Farben dienen nur dazu, die Zugehörigkeit deutlicher zu machen.)
| Selektoren | Spezifizität | ||||||
|---|---|---|---|---|---|---|---|
astyle | - | b ID-Attrib. | - | c Klassen | - | d Elemente |
|
* { ... } |
0 | - | 0 | - | 0 | - | 0 |
li { ... } |
0 | - | 0 | - | 0 | - | 1 |
ul li { ... } |
0 | - | 0 | - | 0 | - | 2 |
ul ol+li { ... } |
0 | - | 0 | - | 0 | - | 3 |
h1 + *[rel=up] { ... } |
0 | - | 0 | - | 1 | - | 1 |
ul ol li.red { ... } |
0 | - | 0 | - | 1 | - | 3 |
li.red.level { ... } |
0 | - | 0 | - | 2 | - | 1 |
#x34y { ... } |
0 | - | 1 | - | 0 | - | 0 |
html>body div#left-menu a:link:hover { } |
0 | - | 1 | - | 2 | - | 4 |
Inline-Style: style=" ... " |
1 | - | 0 | - | 0 | - | 0 |
Sind aufwendige Verschachtelungen vorteilhaft?
An den Beispielen oben kann man schon erkennen, dass sich durch Schachtelung einiger class- und ID-Selektoren relativ schnell eine hohe Spezifizität erreichen lässt. Das hat den Vorteil, dass sich die so codierten Regeln immer und überall durchsetzen. Einfügen zusätzlicher Elementselektoren bringt dagegen keine großen Vorteile.
Gleichzeitig wird aber ein hoher Aufwand notwendig, falls man doch einmal etwas anders deklarieren möchte. Ein einfacher class-Selektor bewirkt dann im Allgemeinen gar nichts. Auch einfache Selektoren, die man bereits vorher deklariert hatte, würden in einem Bereich, in dem eine hochspezifizierte Regel gilt, unter Umständen einfach keine Auswirkung mehr haben.
Mit einem generellen 'Ja' oder 'Nein' lässt sich diese Frage meistens nicht beantworten, es hängt vielmehr vom Einzelfall ab, ob man aufwendigere oder einfachere Selektoren verwendet. Im Folgenden werden einige Beispiele vorgestellt, die das verdeutlichen sollen:
Künstliche Steigerung der Spezifizität
p.class { ... }
#main-content p.class { ... }
html body #main p.class { ... }
Hier wird zunächst eine Klasse für Absätze deklariert, die ganz allgemein für das gesamte Webdokument gilt. Sie hat mit 0-0-1-1 eine durchschnittliche Spezifizität.
Die zweite Regel trifft nur für Absätze innerhalb des div#main-content zu, ist dort aber aufgrund ihrer höheren Spezifizität (0-1-1-1) vorrangig gültig.
Die dritte Regel zeigt einen kleinen Trick, mit dessen Hilfe sich die Spezifizität einer Regel künstlich 'aufblasen' lässt, indem man einfach ein oder zwei Elementselektoren hinzufügt. In diesem Fall wurde die Spezifizität durch html und body auf 0-1-1-3 gesteigert, um dieser Regel wiederum ein höheres Gewicht gegenüber der vorhergehenden zu geben. Dies macht natürlich keinen Sinn, wenn beide Regeln untereinander im selben Stylesheet stehen, sondern z. B. dann, wenn man mit alternativen Stylesheets arbeitet.
Eine Klasse kann mehr bewirken als eine aufwendige Verschachtelung
li.class { ... }
ol li ul li { ... }
Die erste dieser beiden Regeln bezieht sich auf alle Listenelemente eines Webdokuments und erreicht ohne Schachtelung eine Spezifizität von 0-0-1-1.
Wer nun glaubt, durch eine Konstellation wie in der zweiten Regel die Spezifizität steigern zu können, täuscht sich aber. Diese Regel bezieht sich auf alle Listenelemente von ungeordneten Listen, wenn diese innerhalb von geordneten Listen auftreten. Da die Spezifizität von Elementselektoren nur relativ gering ist, erreicht diese Regel eine Spezifizität von nur 0-0-0-4, sie ist also in jedem Falle nachrangig.
Zusätzlich wird durch diese Regel die angestrebte Trennung von Darstellung und Inhalt zu einem gewissen Grad unterlaufen. Falls einmal die geordnete Liste im Quellcode in eine ungeordnete umgewandelt wird, ändert sich dadurch ungewollt auch die Darstellung der nachgeordneten Liste.
Mehrdeutigkeiten bei Nachfahrenselektoren
ol li { ... }
Diese Regel gilt nicht nur für alle Listenelemente einer geordneten Liste, sondern generell für alle Elemente li, die sich innerhalb einer ol-Liste befinden, auch wenn sie Teil einer ungeordneten Liste ul sind wie im vorangegangenen Beispiel. Optimal für Fälle dieser Art geeignet ist der Kindselektor, der aber noch nicht von allen Browsern unterstützt wird. Deshalb stellt auch hier der Klassenselektor vorläufig die beste Lösung dar.
Man sieht also, dass die Spezifizität nicht etwas ist, das der Webautor passiv zu erdulden hat, sondern etwas, das sich auch aktiv beeinflussen lässt, um bestimmte Designziele zu erreichen.
Anmerkung 1:
Die korrekte Übersetzung des englischen Wortes specificity ist Spezifizität, nicht Spezifität. Im Deutschen wird der Ausdruck von dem Verb spezifizieren abgeleitet.
Anmerkung 2:
In CSS 2 wird die Spezifizität mit Hilfe einer dreistelligen Ziffernfolge erklärt. Die Erläuterung auf dieser Seite stützt sich bereits auf die zukünfige Spezifikation CSS 2.1, die in diesem Punkt (aber auch vielen anderen) an das aktuelle Verhalten der Browser angepasst wurde. Dort wird die Spezifizität mit 4 Ziffern berechnet.
CSS Spezifität berechnen – das Tupel aus vier Zahlen
Die CSS Spezifität eines Selektors ist ein Tupel (a, b, c, d), kein Punktwert. a ist 1, wenn die Deklaration im style-Attribut steht, sonst 0. b zählt die ID-Selektoren, c die Klassen-, Attribut- und Pseudoklassen-Selektoren, d die Typ- und Pseudoelement-Selektoren. Der Universalselektor * und Kombinatoren zählen nicht. Verglichen wird von links nach rechts: Erst wenn a gleich ist, zählt b, und so weiter. Eine einzige ID schlägt deshalb beliebig viele Klassen, und eine einzige Klasse beliebig viele Typselektoren – zehn div hintereinander ergeben (0,0,0,10), eine Klasse (0,0,1,0), und die Klasse gewinnt.
| Selektor | a | b | c | d |
|---|---|---|---|---|
* | 0 | 0 | 0 | 0 |
li | 0 | 0 | 0 | 1 |
ul li a | 0 | 0 | 0 | 3 |
a:hover | 0 | 0 | 1 | 1 |
.nav a.aktiv | 0 | 0 | 2 | 1 |
#kopf .nav a | 0 | 1 | 1 | 1 |
style="…" | 1 | 0 | 0 | 0 |
Selektoren und ihr Gewicht – was zählt, was nicht
Zu c zählen Klassen (.aktiv), Attributselektoren ([href], [type="text"]) und Pseudoklassen (:hover, :first-child, :lang(de)). Zu d zählen Typselektoren (p, a) und Pseudoelemente (:first-letter, :before). [id="kopf"] ist ein Attributselektor und zählt zu c, obwohl er dasselbe Element trifft wie #kopf – ein Weg, eine ID mit geringerem Gewicht anzusprechen. Kombinatoren wie das Leerzeichen, > und + haben kein Gewicht; ul > li und ul li sind gleich spezifisch.
Stellung in der Kaskade – wann die Spezifität überhaupt zählt
Die CSS Spezifität ist die dritte Stufe: Die Kaskade prüft in dieser Reihenfolge: Herkunft und Wichtigkeit, dann Spezifität, dann Reihenfolge im Quelltext. Spezifität entscheidet also nur zwischen Deklarationen derselben Herkunft mit derselben Wichtigkeit. Eine Autorregel mit !important schlägt jede normale Autorregel, egal wie spezifisch. Und bei gleicher Spezifität gewinnt die spätere Deklaration – der Grund, warum die Reihenfolge der Stylesheets im Dokument eine Rolle spielt.
Beispiele: CSS Spezifität im Alltag
a { color: navy; } /* (0,0,0,1) */
.nav a { color: black; } /* (0,0,1,1) gewinnt gegen a */
#kopf a { color: gray; } /* (0,1,0,1) gewinnt gegen .nav a */
.nav .liste .eintrag a { … } /* (0,0,3,1) verliert gegen #kopf a */Der letzte Selektor ist der häufigste Fehler: Wer mit immer längeren Klassenketten gegen eine ID anrechnet, verliert immer. Der Ausweg ist nicht mehr Gewicht, sondern weniger: die ID im Stylesheet durch eine Klasse ersetzen, damit alle Regeln auf derselben Ebene konkurrieren.
Häufige Fehler beim Berechnen der CSS Spezifität
- Die vier Zahlen als eine Zahl lesen: (0,0,1,0) ist nicht „10“, und elf Typselektoren ergeben (0,0,0,11), nicht (0,0,1,1).
!importantals Spezifität verstehen. Es gehört zur Wichtigkeit, einer früheren Stufe der Kaskade.- Vererbte Werte mitrechnen. Ein vererbter Wert hat keine Spezifität; er greift erst, wenn keine Regel das Element trifft.
- Die Reihenfolge vergessen: Bei gleichem Tupel gewinnt die spätere Deklaration – auch über Dateigrenzen hinweg.
Häufige Fragen
Wie berechnet man die Spezifität eines CSS-Selektors?
Man zählt vier Werte: a = 1 für Inline-Stil im style-Attribut, b = Anzahl der IDs, c = Anzahl der Klassen, Attributselektoren und Pseudoklassen, d = Anzahl der Typselektoren und Pseudoelemente. Verglichen wird von links nach rechts.
Schlägt eine ID immer eine Klasse?
Ja. Eine ID zählt in Position b, Klassen in Position c. Da von links nach rechts verglichen wird, gewinnt (0,1,0,0) gegen jede Anzahl von Klassen, etwa (0,0,7,0).
Welche Spezifität hat !important?
Keine. !important gehört zur Wichtigkeit, die die Kaskade vor der Spezifität prüft. Eine Deklaration mit !important gewinnt gegen jede normale Deklaration derselben Herkunft, unabhängig vom Selektor.
Zählt der Universalselektor * zur Spezifität?
Nein. * hat die Spezifität (0,0,0,0). Auch Kombinatoren – Leerzeichen, >, + – zählen nicht.
Was passiert bei gleicher Spezifität?
Dann gewinnt die Deklaration, die später im Quelltext steht – innerhalb eines Stylesheets und über mehrere Stylesheets hinweg in der Reihenfolge, in der sie eingebunden sind.
Wie spezifisch ist ein Inline-Stil?
Das style-Attribut zählt in Position a und hat damit (1,0,0,0). Es gewinnt gegen jeden Selektor im Stylesheet; nur !important schlägt es.
Warum gewinnt meine lange Klassenkette nicht gegen die ID?
Weil Klassen in Position c zählen und IDs in Position b. Egal wie viele Klassen aneinandergereiht werden – die Position b der ID wird zuerst verglichen und ist höher. Der Ausweg ist, die ID im Stylesheet durch eine Klasse zu ersetzen.
Haben Pseudoklassen und Pseudoelemente dieselbe Spezifität?
Nein. Pseudoklassen wie :hover zählen wie Klassen in Position c, Pseudoelemente wie :first-letter wie Typselektoren in Position d.