Mehr reale Anwendungsfälle für :has()

Avatar of Liam Johnston
Liam Johnston am

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

Die Pseudo-Klasse :has() ist, ohne Frage, mein neues Lieblings-CSS-Feature. Ich weiß, dass es für viele von Ihnen auch so ist, zumindest für diejenigen unter Ihnen, die die State of CSS-Umfrage durchgeführt haben. Die Möglichkeit, Selektoren umgekehrt zu schreiben, gibt uns mehr Superkräfte, an die ich nie gedacht hätte.

Ich sage „mehr Superkräfte“, weil es bereits eine Menge wirklich erstaunlicher, cleverer Ideen von einer Reihe superkluger Leute gab, wie zum Beispiel

Dieser Artikel ist kein endgültiger Leitfaden für :has(). Er soll auch nicht wiederholen, was bereits gesagt wurde. Es ist nur ich (Hallo 👋), der für einen Moment auf den Zug aufspringt, um einige der Wege zu teilen, wie ich :has() wahrscheinlich in meiner täglichen Arbeit verwenden werde ... sobald die Browserunterstützung gut genug ist. (Firefox ist der letzte Ausreißer, wird es aber unterstützen, was unmittelbar bevorsteht.)

Wenn es soweit ist, können Sie darauf wetten, dass ich :has() überall verwenden werde. Hier sind einige reale Beispiele von Dingen, die ich kürzlich gebaut habe und dachte mir: „Mensch, das wird so viel schöner, wenn :has() vollständig unterstützt wird.“

Vermeiden Sie es, über Ihre JavaScript-Komponente hinauszugreifen

Haben Sie jemals eine interaktive Komponente erstellt, die manchmal Stile an anderer Stelle auf der Seite beeinflussen muss? Nehmen Sie das folgende Beispiel, bei dem <nav> ein Mega-Menü ist und dessen Öffnen die Farben des <header>-Inhalts darüber ändert.

Ich habe das Gefühl, dass ich so etwas *ständig* tun muss.

Dieses spezielle Beispiel ist eine React-Komponente, die ich für eine Website erstellt habe. Ich musste mit document.querySelector(...) „außerhalb“ des React-Teils der Seite greifen und eine Klasse auf dem <body>, <header> oder einer anderen Komponente umschalten. Das ist nicht das Ende der Welt, aber es fühlt sich schon etwas doof an. Selbst auf einer reinen React-Website (z. B. einer Next.js-Website) müsste ich mich entscheiden, ob ich einen menuIsOpen-Zustand weiter oben im Komponentenbaum verwalte oder die gleiche DOM-Elementauswahl durchführe – was nicht sehr React-artig ist.

Mit :has() verschwindet das Problem

header:has(.megamenu--open) {
  /* style the header differently if it contains 
    an element with the class ".megamenu--open"
  */
}

Kein Herumfummeln mehr mit anderen Teilen des DOM in meinen JavaScript-Komponenten!

Bessere Tabellen-Striping-UX

Das Hinzufügen von abwechselnden Zeilen „Streifen“ zu Ihren Tabellen kann eine schöne UX-Verbesserung sein. Sie helfen Ihren Augen, den Überblick zu behalten, auf welcher Zeile Sie sich befinden, wenn Sie die Tabelle durchsuchen.

Aber meiner Erfahrung nach funktioniert das bei Tabellen mit nur zwei oder drei Zeilen nicht gut. Wenn Sie zum Beispiel eine Tabelle mit drei Zeilen in <tbody> haben und jede „gerade“ Zeile „streifen“, könnten Sie am Ende nur einen Streifen haben. Das ist ein Muster nicht wirklich wert und könnte Benutzer dazu bringen, sich zu fragen, was an dieser einen hervorgehobenen Zeile so besonders ist.

Unter Verwendung dieser Technik, bei der Bramus :has() verwendet, um Stile basierend auf der Anzahl der Kinder anzuwenden, können wir Tabellenstreifen anwenden, wenn es mehr als sagen wir drei Zeilen gibt.

Möchten Sie es noch ausgefallener gestalten? Sie könnten sich auch entscheiden, dies nur zu tun, wenn die Tabelle mindestens eine bestimmte Anzahl von Spalten hat.

table:has(:is(td, th):nth-child(3)) {
  /* only do stuff if there are three or more columns */
}

Entfernen Sie bedingte Klassenlogik aus Vorlagen

Ich muss oft ein Seitenlayout ändern, je nachdem, was auf der Seite vorhanden ist. Nehmen Sie das folgende Grid-Layout, bei dem sich die Platzierung des Hauptinhalts je nach vorhandenem Sidebar in Grid-Bereichen ändert.

Layout with left sidebar above a layout with no sidebar.

Das kann davon abhängen, ob Geschwisterseiten im CMS eingestellt sind. Normalerweise würde ich dies mit Vorlagenlogik tun, um bedingt BEM-Modifikatorklassen zum Layout-Wrapper hinzuzufügen, um beide Layouts zu berücksichtigen. Dieser CSS-Code könnte ungefähr so aussehen (responsive Regeln und anderes Zeug der Kürze halber weggelassen)

/* m = main content */
/* s = sidebar */
.standard-page--with-sidebar {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page--without-sidebar {
  grid-template-areas: '. m m m m m m m m m . .';
}

CSS-technisch ist das natürlich völlig in Ordnung. Aber es macht den Vorlagencode etwas unübersichtlich. Je nach Ihrer Vorlagensprache kann es ziemlich hässlich werden, bedingt eine Reihe von Klassen hinzuzufügen, besonders wenn Sie dies auch mit vielen Kindelementen tun müssen.

Im Gegensatz dazu ein Ansatz, der auf :has() basiert

/* m = main content */
/* s = sidebar */
.standard-page:has(.sidebar) {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page:not(:has(.sidebar)) {
  grid-template-areas: '. m m m m m m m m m . .';
}

Ehrlich gesagt, das ist CSS-technisch nicht viel besser. Aber das Entfernen der bedingten Modifikatorklassen aus der HTML-Vorlage ist meiner Meinung nach ein schöner Gewinn.

Es ist leicht, an Mikrodesign-Entscheidungen für :has() zu denken – wie eine Karte, wenn sie ein Bild enthält – aber ich denke, es wird auch für diese Makro-Layout-Änderungen wirklich nützlich sein.

Besseres Spezifitätsmanagement

Wenn Sie meinen vorherigen Artikel gelesen haben, wissen Sie, dass ich ein Liebhaber der Spezifität bin. Wenn Sie, wie ich, nicht möchten, dass Ihre Spezifitätswerte explodieren, wenn Sie :has() und :not() in Ihren Stilen verwenden, stellen Sie sicher, dass Sie :where() verwenden.

Das liegt daran, dass die Spezifität von :has() auf dem spezifischsten Element in seiner Argumentliste basiert. Wenn Sie also etwas wie eine ID darin haben (warum, bin ich mir nicht sicher!), wird Ihr Selektor in der Kaskade schwer zu überschreiben sein.

Auf der anderen Seite ist die Spezifität von :where() immer Null und erhöht niemals den Spezifitätswert.

/* specificity score: 0,1,0.
  Same as a .standard-page--with-sidebar 
  modifier class
*/
.standard-page:where(:has(.sidebar)) {
  /* etc */
}

Die Zukunft ist hell

Dies sind nur einige Dinge, die ich kaum erwarten kann, in der Produktion nutzen zu können. Der CSS-Tricks-Almanach enthält auch viele Beispiele. Was möchten Sie mit :has() machen? Was für reale Beispiele sind Ihnen begegnet, bei denen :has() die perfekte Lösung gewesen wäre?