Selektoren in CSS/Logische Pseudoklassen
Bis jetzt hast du gelernt, wie man Elemente mit einfachen Selektoren auswählt, sie mit Kombinatoren verknüpft und sogar Pseudo-Elemente erzeugst. Was aber, wenn du mehrere Muster gleichzeitig abgleichen oder bestimmte Elemente ausschließen willst, ohne lange, sich wiederholende Selektoren zu schreiben?
Hier kommen die Pseudo-Klassen für „logische Kombinationen”[1] in CSS ins Spiel: :is()
, :not()
und :where()
.
Mit ihnen kann man Selektoren gruppieren, Elemente herausfiltern oder Stile breit anwenden, ohne die Spezifität mehr als nötig zu erhöhen.
In diesem Artikel zeigen wir, wie man diese neuen CSS-Selektoren anwendet und damit kürzeres, klareres CSS schreibt, das weniger fehleranfällig ist.
Inhaltsverzeichnis
In CSS können mehrere Selektoren, die dieselben Deklarationen haben, in einer durch Kommas getrennten Selektor-Liste zusammengefasst werden. Vor und/oder nach dem Komma können Leerzeichen oder Zeilenumbrüche stehen.
h1 { font-family: sans-serif }
h2 { font-family: sans-serif }
h3 { font-family: sans-serif }
// entspricht:
h1, h2, h3 { font-family: sans-serif }
Dies kann es schwierig machen, CSS zu schreiben, das neue Selektoren verwendet und dennoch in älteren Browsern, die diese noch nicht kennen, korrekt funktioniert.
Selektoren verkürzen mit :is()
Die matches-any-Pseudoklasse :is() ist eine funktionale Pseudoklasse, die eine <forgiving-selector-list> als einziges Argument akzeptiert. Diese <forgiving-selector-list> analysiert stattdessen jeden Selektor in der Liste einzeln und ignoriert einfach diejenigen, die nicht analysiert werden können, sodass die verbleibenden Selektoren weiterhin verwendet werden können.[3]
Mit ihr können nun umfangreiche Regelsätze übersichtlich verkürzt werden:
.entry .entry-content blockquote,
.entry .entry-content figure,
.entry .entry-content p,
.entry .entry-content pre,
.entry .entry-content ul,
.entry .entry-content ol,
.entry .entry-content h1,
.entry .entry-content h2 {
max-width: 52rem;
margin-left:auto;
margin-right: auto;
}
.entry .entry-content :is(blockquote, figure, p, pre, ul, ol, h1, h2) {
max-width: 50em;
margin-inline:auto;
}
Selektoren, die innerhalb der :is()
-Pseudoklasse angegeben werden, werden in der Spezifität des Selektors berücksichtigt. Wenn man das nicht wünscht, kann statt dessen die :where()
-Pseudoklasse verwendet werden, die sich im Übrigen identisch zu :is()
verhält.
article :is(header, footer) > p { … }
:is(:link, :visited) { color: red; }
Das Beispiel soll die Schriftfarbe aller Verweise auf rot setzen, wird aber nicht ausgeführt. (Hinweis: Das Beispiel ist äquivalent zu any-link bzw. [href]).
Hinweis bei Verwendung von CSS-Namespaces: Die Selektoren innerhalb von :is()
beachten einen definierten Standard-Namespace nur, wenn sie explizit einen Typ- oder Universalselektor enthalten. Ein mögliches Namespace-Präfix außerhalb des :is()
gilt dann nicht innerhalb von :is()
!
*|*:is(:hover, :focus) { color: red }
*|*:is(*:hover, *:focus) { color: red }
Im ersten Beispiel wird jedem überfahrenen oder fokussierten Element die Schriftfarbe rot zugewiesen, egal in welchem Namespace es sich befindet. Im zweiten Beispiel betrifft dies ausschließlich die Elemente im Standard-Namespace, da der Universalselektor innerhalb von :is explizit angegeben wurde.
Elemente ausschließen mit :not()
Während :is()
die Auswahl eingrenzt, hilft :not(), Dinge auszuschließen, ohne wiederholten Code schreiben zu müssen. Die Pseudoklasse erwartet als Argument einen beliebigen Selektor. Sie spricht dann alle diejenigen Elemente an, auf die das Argument nicht zutrifft.
Stell dir vor, du gestaltest einen Blog. Du möchtest, dass der Großteil des Textes gleich aussieht – aber nicht alles. Überschriften müssen hervorstechen, Zitate benötigen einen besonderen Abstand, Bilder sollten vielleicht nicht dem gleichen Rhythmus wie Absätze folgen. Ohne :not() müsste man lange, sich wiederholende Regeln schreiben, um jeden Elementtyp einzeln abzudecken. Mit :not() kann man die Logik umkehren: Du gestaltest alles standardmäßig und gibst dann einfach an, was nicht gestaltet werden soll.
article :not(h1, h2, h3) {
margin-top: 1em;
}
In diesem Beispiel würden alle Kindelemente des article, die keine Überschriften h1-h3 sind, einen oberen Rand von 1em erhalten. Dies ist etwa sinnvoll, wenn dieses Element neben Überschriften auch Absätze, Listen und Bilder enthält.
a:not([href*="example."]) { background: red; }
In diesem Beispiel werden alle Link-Elemente, deren href-Attribut nicht die Zeichenkette „example.“ enthält, mit einem roten Hintergrund versehen.
:is(nav, aside) :not(a) {
opacity: 0.6;
}
In Level 3 der Selektoren-Spezifikation durfte :not()
nur einen einfachen Selektor enthalten, und :not() durfte nicht geschachtelt werden. Die meisten Browser unterstützen mittlerweile den Level 4, der auch Kombinatoren und Selektorenlisten zulässt und das Schachtelungsverbot aufhebt. Pseudoelemente (wie z. B. ::after
) sind allerdings nicht zulässig.
Spezifität niedrig halten mit :where()
Die specificity-adjustment-Pseudoklasse :where() ist eine Variante der funktionalen Pseudoklasse :is()
. Der einzige Unterschied ist, dass die Selektoren innerhalb von :where()
nicht zur Spezifität des Gesamtselektors beitragen.
:is(h1, h2, h3) {
margin-bottom: 0.5em;
}
:where(h1, h2, h3) {
margin-bottom: 0.5em;
}
Sie sehen fast gleich aus, aber der große Unterschied liegt in der Spezifität.
- :is() → übernimmt die Spezifität des spezifischsten Selektors im Inneren.
- :where() → hat immer eine Spezifität von Null (wie ein universeller Selektor *).
Die Spezifität entscheidet, welche CSS-Regel Vorrang hat, wenn zwei Regeln dasselbe Element betreffen.
- Verwende :is(), wenn du möchtest, dass sich der gruppierte Selektor normal verhält (einschließlich Spezifität).
- Verwende :where(), wenn du Standardstile festlegen möchtest, die später leicht überschrieben werden können.
Eltern nach Kindern stylen mit :has()
Jahrelang konnte CSS nur den Element-Baum nach unten durchlaufen: Man konnte Kinder, Enkelkinder und Nachkommen stylen. Aber man konnte nie nach oben schauen – nie sagen: „Gib mir ein Elternteil, das ein bestimmtes Kind hat.“
Deshalb wirkt :has() fast wie Zauberei: Endlich kann CSS Beziehungen in beide Richtungen betrachten.
Denke an eine Navigationsleiste: Einige Menüpunkte sind einfache Links, andere öffnen ein Dropdown-Menü. Bisher musste man Klassen wie .has-dropdown
in den HTML-Code einfügen, um diese unterschiedlich zu gestalten.
Mit :has() benötigt man diese zusätzlichen Klassen nicht mehr:
nav li:has(ul)::after {
content: " ▾";
font-size: 0.8em;
color: #555;
}
Jedes li-Element, das als Kind eine weitere ul hat, wird mit einem Pfeil ausgestattet, damit Nutzer einen Hinweis auf die weitere Ebene haben.
Bei Formularen musste man sich oft verrenken: Logischerweise sollte ein label vor dem Eingabefeld stehen, damit man es erst lesen und dann Daten eingeben kann. Um labels entsprechend der Eingabe zu gestalten, benötigte man den Geschwister-Kombinator, der aber nur nachfolgende Elemente selektierte.
Mit has() geht das leichter:
input:required:user-invalid {
background-color: #fed8cd;
}
label:has(+ input:required)::after {
content: " *";
color: red;
font-weight: bold;
}
label:has(+ input:user-valid)::after {
content: " ✓";
color: green;
font-weight: bold;
}>
Die Pseudoklassen :user-valid, bzw. :user-invalid überprüfen nach einer Eingabe, ob diese valide ist. Hier wird das vorangestellte label mit einem ::after-Pseudoelement erweitert und so die (mangelnde) Korrektheit der Eingabe gekennzeichnet.
→ Formulare/Benutzereingaben zugänglich gestalten
Siehe auch
Dynamische Pseudoklassen selektieren Elemente aufgrund einer Benutzeraktion. Sie finden sich in vielen Bereichen und verrichten dort ihre Dienste:
- CSS-Selektorenalphabetische Referenz für den schnellen Überblick
- Links
Maus- und Tastaturinteraktionen
- :hover und :focus
- :focus-within
- :visited, :link, :any-link
- Akkordeon mit <details>
- :open
- :details-content
- Formulare validieren
- :valid, :invalid
- :in-range, :out-of-range
- Auswahllisten
- :checked, indeterminate
- :picker(select)
- Ausgrauen: readonly vs disabled
- :disabled
- :read-only, :red-write
Weblinks
- ↑ 4. Logical Combinations Selectors Level 4
W3C Working Draft, 11 November 2022 - ↑ 4. Selector Lists Selectors Level 4
W3C Working Draft, 11 November 2022 - ↑ 18.1. <forgiving-selector-list> and <forgiving-relative-selector-list> Selectors Level 4
W3C Working Draft, 11 November 2022