Rolf B: CSS Selektoren - :nth-match() vs :nth-child(... of S)

Beitrag lesen

Die Selektion von HTML-Elementen mit der CSS Pseudoklasse :nth-child() wird schwierig, wenn die Elemente noch zusätzlich über Klassen- oder Attributselektoren eingegrenzt werden sollen und die Kindelement-Zählung nur innerhalb der zusätzlichen Selektoren stattfinden soll.

Um das zu lösen, wurde bereits 2011, im ersten Entwurf der CSS Selectors Level 4 Spezifikation, die Pseudoklasse :nth-match() vorgeschlagen.

Lesen Sie hier, was daraus wurde.


Das Problem

Angenommen, Sie hätten eine Tabelle und möchten die Lesbarkeit dadurch verbessern, dass Sie nach jeder dritten Zeile eine horizontale Linie einfügen. Das gelingt beispielsweise mit

#mytable tbody tr:nth-of-type(3n) {
   border-bottom: thin solid black;
}

Solange alle Rows der Tabelle dargestellt werden, wird auf diese Weise unter jeder dritten Zeile ein Strich gezogen. Aber was ist, wenn diese Tabelle noch eine Filterfunktion hätte, und die sichtbaren Zeilen mit einer Klasse match versehen werden. Die CSS-Regel für eine solche Filterung könnte so aussehen:

#mytable.filtered tbody tr:not(.match) {
   visibility:collapse;
}

Möchte man die Hilfslinien ergänzen, könnte man das so versuchen:

#mytable tbody tr.match:nth-of-type(3n) {
   border-bottom: thin solid black;
}

Aber das funktioniert nicht, weil der Selektor tr.match:nth-of-type(3n) aus drei Teilen besteht (Elementselektor, Klassenselektor, Pseudoklassenselektor), die alle unabhängig voneinander bewertet werden und deren Ergebnis UND-verknüpft wird.

Teil 1: Elementtypselektor tr
Betrachte nur tr-Elemente
Teil 2: Klassenselektor .match
Betrachte nur Elemente mit der Klasse match
Teil 3: strukturelle Pseudoklasse :nth-of-type()
Betrachte nur Elemente, die das 0-te, 3-te, 6-te, 9-te, … ihres Elementtyps sind

In einer Tabelle mit 5 Zeilen, in der die Zeilen 1 und 5 sichtbar sind, würde dieser Selektor auf keine Zeile zutreffen, weil in jeder Zeile entweder der Klassenselektor .match oder der Pseudoklassenselektor :nth-of-type(3n) unzutreffend ist.

Was tun?

Was gebraucht wird, ist ein zweistufiges Vorgehen. Zunächst müssen alle sichtbaren Rows ermittelt werden, und von diesen Rows diejenigen mit gerader Nummer. Bisher ging das nur mit Hilfe von JavaScript.

Tatsächlich gibt es schon seit 2011 einen Vorschlag für eine CSS-Lösung: die :nth-match Pseudoklasse. tr.match:nth-match(3n) würde das Gewünschte leisten. Diese Pseudoklasse wurde allerdings 2013 wieder verworfen, zu Gunsten einer erweiterten Syntax von :nth-child():

:nth-child(An+B of S)

Safari unterstützt das seit 2015. Der Rest der Browserwelt hat sich acht Jährchen Bedenkzeit gegönnt, aber seit März 2023 steht dieser Selektor in Chromium-Browsern (Version 111) zur Verfügung, im April erschien Opera 98 mit diesem Feature und Firefox ist im Mai mit Version 113 nachgezogen (siehe Kompatibilität bei caniuse.com).

Wie ist dieses S zu verstehen?

Dabei handelt es sich um einen CSS Selektor, der die Elemente bestimmt, die für :nth-child berücksichtigt werden sollen. Die Spezifikation ist hier noch etwas im Fluss. Der neueste Entwurf besagt, dass es sich dabei um eine „complex-real-selector-list“ handele, was bedeutet: ein CSS Selektor, in dem außer Pseudoelementen alles verwendet werden kann, selbst Kommata. Der offizielle Working Draft formuliert allgemeiner und spricht von einer "complex-selector-list parsed as a forgiving selector", d.h. die Einschränkung der Pseudoelemente ist nicht vorhanden. Dafür wird aber gefordert, dass für den Fall, dass eine Selektorliste einen ungültigen Selektor enthält, die übrigen Selektoren der Liste trotzdem berücksichtigt werden sollen.

Für unser Beispiel mit der gefilterten Zebrastreifentabelle würden wir also schreiben können:

#mytable.filtered tbody tr:nth-child(3n of .match) {
   background-color: #e0e0e0;
}

wodurch dann nur die Elemente für die nth-child-Zählung berücksichtig werden, die die Klasse .match tragen.

Wo überall geht das?

Die gleiche Erweiterung existiert für :nth-last-child().

Was es nicht gibt, ist die of-Erweiterung für :nth-of-type. Das hat einen guten Grund: die :nth-of-type()-Pseudoklasse ist ein Sonderfall von :nth-child(… of S):

#mytable tbody tr:nth-of-type(3n of .match) { ... }

Ziehen Sie den Elementselektor für tr einfach in die Klammer hinein, wodurch :nth-child ganz automatisch die Selektion des Elementtyps mit übernimmt.

#mytable tbody :nth-child(3n of tr.match) { ... }