Dieser Beitrag ist der dritte in einer Reihe über die Macht von CSS.
Artikelserie
- SVG-Hintergründe einfärben
- Dropdown-Menüs
- Logisches Styling basierend auf der Anzahl der vorhandenen Elemente (dieser Beitrag)
Wussten Sie schon, dass CSS Turing-vollständig ist? Wussten Sie schon, dass Sie damit ziemlich ernsthafte logische Stylings durchführen können? Nun, das können Sie! Sie müssen nicht alle Ihre logikbasierten Styling-Regeln in JavaScript festlegen oder nicht einmal JavaScript verwenden, um Klassen festzulegen, gegen die Sie stylen. In vielen Fällen kann CSS dies selbst erledigen. Ich entdecke jeden Tag neue CSS-Tricks, und das lässt mich es nur noch mehr lieben.
Dieses Jahr habe ich bei Bustle Digital Group angefangen zu arbeiten. In den Medien, wie bei vielen Produkten, baut das Engineering-Team eine Plattform, die alle Anwendungsfälle unterstützen soll. Unser CMS bietet Autoren und Redakteuren die Möglichkeit, Artikel zu erstellen sowie Seiten zu kuratieren und die Einbindung von Anzeigen zu steuern.
Im Gegensatz zur Arbeit mit einer statischen Website hat das Engineering-Team keine vollständige Kontrolle über die vom Benutzer eingegebenen Daten, sodass Designentscheidungen und Regeln für eine gute Benutzererfahrung getroffen werden müssen. Einige dieser Szenarien, mit denen wir im digitalen Medienbereich konfrontiert waren, haben mich wirklich dazu inspiriert, nach Möglichkeiten zu suchen, CSS zur Lösung dieser UI-Herausforderungen einzusetzen, und da kamen Lösungen, die diese Idee beinhalten, wirklich in meinen Fokus.
Schauen wir uns also einige Beispiele an!
Beispiel 1: Binäre Zustände
Ein oft vergessener und *sehr* nützlicher Selektor ist der Pseudo-Selektor :empty. Er ermöglicht es Ihnen, Elemente basierend darauf zu stylen, ob sie Inhalt enthalten oder nicht. Hallo leere Zustände! Leere Zustände sind eine großartige Möglichkeit, Ihre Benutzer zu erreichen und Persönlichkeit in Ihre App zu bringen, und Sie können diese Persönlichkeit direkt aus Ihrem CSS einbringen.
In diesem Beispiel haben wir eine Liste von einem Benutzer. Dies könnten Beiträge sein, die der Benutzer (als Autor) veröffentlicht hat, oder gespeicherte Artikel, die ein Benutzer (als Redakteur) gespeichert hat. Die Anwendungsfälle sind hier wirklich endlos. Anstatt JavaScript einzubinden, können wir Pseudo-Elemente verwenden, um Bilder, Stile und Text einzufügen.

Unsere Lösung hier sind bloße drei Codezeilen
div:empty:after {
content: 'oh no...';
}
Sie können auch ein :before Pseudo-Element hinzufügen, um Bilder oder andere gewünschte Inhalte einzufügen. Alternativ kann der :not Pseudo-Selektor in *Kombination* mit :empty verwendet werden, um eine :not(:empty) Regel zu erstellen und alle Elemente zu stylen, die nicht leer sind und daher Kinder enthalten.
Siehe den Pen Empty States von Una Kravets (@una) auf CodePen.
Hinweis: Diese Demo dient nur zur Veranschaulichung. Es wird *nicht* empfohlen, Inhalte für Barrierefreiheitszwecke in Pseudo-Elemente einzufügen. Sie können die gleiche Technik verwenden, um :empty oder :not(:empty) Elemente anzusprechen, um Stile auf Kindelemente anzuwenden, die für Screenreader zugänglicher sind.
Erweiterte numerische Auswahl
Das war ein schönes, einfaches Beispiel, aber wir können viel komplexer werden als diese binäre Wahl von Kindelementen in CSS, und dazu verwenden wir den Pseudo-Selektor :nth-child! CSS-Tricks hat ein großartiges Werkzeug, das Ihnen hilft, mit der :nth-child Auswahl zu testen und zu experimentieren, und es kann sich als sehr nützlich erweisen, wie einige der Beispiele zeigen werden.
Aber bevor wir zu diesen kommen, wie funktioniert das genau?
Der Kern des Codes ist dieser, wobei div für ein beliebiges Geschwisterelement steht und x für die Zahl, die wir zur Bestimmung von Stilumbrüchen verwenden.
div:first-child:nth-last-child(n + x),
div:first-child:nth-last-child(n + x) ~ div
Die Verwendung von :nth-last-child anstelle von :nth-child zur Auswahl ermöglicht es uns, vom *Ende* einer Serie anstatt vom Anfang auszugehen. Wenn wir :nth-last-child(n + x) auswählen, wählen wir den x-Wert, beginnend vom Ende. Wenn x = 3, würde das so aussehen

:nth-last-child(3) das dritte Element vom Ende der Liste auswählt.Wenn wir nun Werte von n + 3 zählen wollen, wählen wir alle Elemente aus, die **3 oder mehr als 3 vom Ende** entsprechen. Beginnend mit n = 0 (was 0 + 3 bedeuten würde, und das 4. Element wäre das erste vom Ende nach 3). Es sieht so aus

:nth-last-child(n+3) alle Elemente auswählt, die 3 oder mehr als 3 vom Ende entsprechen.Das ist ein guter Anfang, aber die Idee hier ist, *alle* Elemente bedingt zu stylen, basierend darauf, wie viele vorhanden sind. Wir müssen also mit diesen Bedingungen arbeiten, aber *alle* Elemente auswählen. Beginnen wir mit der Auswahl des ersten Elements. Wir müssen eine Bedingung erstellen, um zu sehen, ob die gesamte Auswahl für das Styling qualifiziert ist, und dann mit diesem ersten Geschwisterelement beginnen.

Ohje. Wir haben zu diesem Zeitpunkt nur das erste Element ausgewählt und wollen *alle Elemente* auswählen. Glücklicherweise können wir den superpraktischen benachbarten Geschwisterselektor (~) dafür verwenden!

:first-child:last-child(n + 3) ~ * anpassen, werden alle Elemente außer dem ersten ausgewählt, wie wir es wollen.Nun, jetzt sehen Sie alle Elemente, die dem ersten Element folgen, ausgewählt, aber wir vermissen das erste, also müssen wir zwei Selektoren verwenden, und somit wird die endgültige Antwort

Beispiel 2: Listenformatierung
Sagen wir, Sie möchten am Ende eines Artikels einige Credits auflisten. Sie haben Platz zum Füllen, und die meisten Artikel haben eine kleine Anzahl von Credits, aber es gibt Ausnahmen, die eine hohe Produktionsqualität haben und bei denen viele Leute an der Erstellung beteiligt waren. Wir wollen sicherstellen, dass beide gute visuelle Erlebnisse bieten, und das können wir allein mit CSS tun.
Hier ist der Plan: Wenn es vier oder weniger Credits gibt, listen wir sie im Bullet-Format auf. Lassen Sie sie vertikalen Raum einnehmen, um den Block entsprechend zu füllen. Sobald wir fünf oder mehr Credits aufgelistet haben, wandeln wir diese Liste in ein horizontales Format um, um den Leser nicht zu überfordern. Das ist schließlich eine kleine Credit-Box!

Wir können die Anzahl der verfügbaren Elemente überprüfen und sie als block-Elemente stylen, *bis* wir unsere Grenze erreichen. An diesem Punkt wechseln wir zum inline-Styling und fügen ein Pseudo-Element hinzu, um die Daten visuell zu trennen.
/* 5 or more items display next to each other */
li:first-child:nth-last-child(n + 5),
li:first-child:nth-last-child(n + 5) ~ li {
display: inline;
}
/* Adds semicolon after each item except the last item */
li:first-child:nth-last-child(n + 5) ~ li::before {
content: ';';
margin: 0 0.5em 0 -0.75em;
}
:nth-first-child:nth-last-child(n + 5) ermöglicht es uns zu sagen: „Beginne mit dem ersten Kind und wende Stile auf dieses Kind und jedes Geschwisterelement danach an, wenn das ursprüngliche Kind fünf oder mehr Geschwister hat.“ Klingt das verwirrend? Nun, es funktioniert.
li:first-child:nth-last-child(n + 5) wählt das erste Listenelement aus, und li:first-child:nth-last-child(n + 5) ~ li wählt jedes Listenelement nach dem anfänglichen aus.
Siehe den Pen vrQBMv von Una Kravets (@una) auf CodePen.
Beispiel 3: Bedingter Karussell
Mit dieser Technik stylen wir ein Karussell, um es responsiv zu gestalten. Bei großer Größe möchten Sie, dass es in der Mitte der Seite zentriert ist, wenn es drei Elemente enthält. Aber wenn es genügend Elemente hat, um den Bildschirm horizontal zu füllen, lassen Sie es linksbündig für den Benutzer zum Durchwischen ausrichten.

Was wir hier tun können, ist, die Elemente so zu dehnen, dass sie auf den Bildschirm passen, es sei denn, wir haben zu viele Elemente und sie würden einen Überlauf erfordern. An diesem Punkt setzen wir voll auf diesen Überlauf und stellen die Karussellfunktionen zur Schau, indem wir Scrollbarkeit mit Pfeilen signalisieren und den Abstand zwischen den Elementen erhöhen. Darüber hinaus fügen wir einen festen Pfeil-Button hinzu, um zu zeigen, dass wir die Elemente durchscrollen können, und binden JavaScript-Ereignisse an, um das Karussell scrollbar zu machen.
Wir können dasselbe wie oben in Bezug auf die Technik tun, aber wir werden *auch* nur das first-child verwenden, um ein arrow-Div zu erkennen und es in der UI anzuzeigen. Das HTML würde so aussehen
<ul>
<li>
<div class="box">1</div>
</li>
<li>
<div class="box">2</div>
</li>
...
<button class="arrow">——></button>
</ul>
Es ist nicht ideal, leere Elemente im DOM zu haben, aber arbeiten Sie mit mir. Es ist immer noch ein cleverer Hack. Wir stylen den .arrow-Button so, dass er für das DOM und Screenreader mit visibility: hidden unsichtbar ist, es sei denn, die Bedingungen gelten (in diesem Fall, wenn vier oder mehr Elemente vorhanden sind). An diesem Punkt geben wir ihm eine sichtbare Anzeige (display: block), Stil und positionieren ihn entsprechend.
li:first-child:nth-last-child(n + 5) ~ .arrow {
display: block;
position: sticky;
...
}
Siehe den Pen Box Alignment von Una Kravets (@una) auf CodePen.
Weitere Informationen!
Bei meiner Recherche für diesen Beitrag habe ich einen ausgezeichneten Beitrag von Heydon Pickering zu dieser Technik namens Quantity Queries und ein weiteres Beispiel von Lea Verou entdeckt! In der Kommentarzeile von Heydons Beitrag merkt Paul Irish an, dass dies eine langsamere Methode zur Auswahl von Elementen ist, also verwenden Sie sie vielleicht mit Vorsicht.
Toller Artikel! Ich habe die gleiche Technik verwendet, um sicherzustellen, dass ich eine gut gestylte Shortcut-Leiste hatte. Tolle Lösung!
https://medium.com/@bramdijkhuis/context-aware-list-items-witch-css-well-sort-of-2a433500fb15
Fantastischer Artikel. Habe eine sehr praktische neue CSS-Technik gelernt. Danke fürs Teilen.
Ich hasse es, ein HTML-Markup-Nörgler zu sein, aber im letzten Karussell-Beispiel hast du einen „button“ als direkten Nachkommen eines „ul“-Elements. Soweit ich weiß, erlaubt die Spezifikation nur „li“-Elemente als direkte Nachkommen von „ul“-Elementen.
Gute Arbeit, wie üblich. Ich liebe wirklich, was Sie tun und wie Sie Entwickler motivieren, neue Dinge zu erforschen und auszuprobieren.
Eine kleine Anmerkung: Sie haben geschrieben: „Das würde mit dem vierten von hinten beginnen, beginnend mit n = 1“
Es ist nicht ganz richtig, da
ntatsächlich mit 0 und nicht mit 1 beginnt, also werden in diesem Beispiel Stile ab dem dritten Element von hinten angewendet und nicht ab dem vierten.Zitat aus der Spezifikation: „…Die :nth-last-child(an+b) Pseudo-Klassennotation repräsentiert ein Element, das für jeden positiven ganzzahligen oder Nullwert von n an+b-1 Geschwister nach sich im Dokumentenbaum hat.“ (Link: https://drafts.csswg.org/selectors-3/#nth-last-child-pseudo)
Ich aktualisiere das gerade! Die Grafik ist korrekt, da es sich um das erste Element *nach* dem dritten von hinten handelt, was das vierte von hinten wäre, aber ich habe mich verschrieben und
n = 1stattn = 0geschrieben.Es ist möglich, dass ich etwas übersehe, aber ich glaube, das Diagramm/die Grafik/das Bild ist auch nicht korrekt, da der Stil ab dem dritten Element von hinten und nicht ab dem vierten angewendet wird. Da es lautet: 0 + 3, 1 + 3, 2 + 3, usw.
Hier ist eine kleine Demo, die ich zur Veranschaulichung gemacht habe: https://goo.gl/LPoC42
Das einzige Problem mit dem :empty-Selektor ist, dass er nicht übereinstimmt, wenn das einzige, was im Element enthalten ist, Whitespace ist. Ihr Code muss also in dieser Hinsicht sehr sauber sein. Ähnlich wie bei Pseudo-Elementen können Sie ihn nicht auf Tags anwenden, die keinen entsprechenden schließenden Tag haben.
Ja! Das stimmt.
Weiterführende Lektüre: Heydon Pickerings ALA-Artikel Quantity Queries for CSS https://alistapart.com/article/quantity-queries-for-css
Das Markup im letzten Beispiel ist kein gültiges HTML:
buttondarf kein Kind vonulsein.Wäre dieser „Oh nein“-Zustand nicht für Screenreader unzugänglich?
Ja, daher der Hinweis direkt nach dem Beispiel. :)
Una, hier gibt es einige erstaunliche Dinge. Ich habe versucht, einige dieser Probleme in letzter Zeit zu lösen. Die von Ihnen skizzierten Beispiele sind so gut erklärt, dass ich sie aus einer völlig anderen Perspektive betrachte, wofür ich Ihnen sehr dankbar bin. Danke!
Ich war zuerst verwirrt, weil Sie „benachbarter Geschwisterselektor“ sagten. Das würde nur das zweite Element auswählen.
Was Sie tatsächlich meinen, ist der „allgemeine Geschwisterselektor“ (oder gemäß Selectors Level 4 „subsequent-sibling“).
Und ja, das ist „~“.
Abgesehen davon: netter Trick, danke für den Beitrag :-)
Hallo. Ich habe mich gefragt, was der Zweck der
:first-childSelektoren in den obigen Beispielen ist?Ich führe sie auf CodePen aus, ohne den
:first-childSelektor, und sie (scheinen) sich genauso zu verhalten.Zum letzten Teil des Artikels
„In der Kommentarzeile von Heydons Beitrag merkt Paul Irish an, dass dies eine langsamere Methode zur Auswahl von Elementen ist, also verwenden Sie sie vielleicht mit Vorsicht.“
Dies ist die URL für den Test, den Paul im genannten Artikel eingefügt hat: http://output.jsbin.com/gozula/1/quiet und die Ergebnisse
1. div.box:not(:empty):last-of-type .title: 5.777099609375ms
2. .box–last > .title-container > .title: 3.202880859375ms
3. .box:nth-last-child(-n+1) .title: 6.501708984375ms
Also ist es nicht so schlimm, wie es früher war (#3 500 Mal langsamer als #1). Getestet auf Chrome 70.0.
Ich habe Ihren „Beispiel 2“ etwas weiterentwickelt, indem ich angehängte Semikolons zu allen außer dem letzten Element hinzugefügt habe, wenn sie inline angezeigt werden. Ihr aktuelles Beispiel stellt ihnen Semikolons vor, die allen außer dem ersten Element vorangestellt werden. Das Layout, die Abstände und der Zeilenumbruch führen zu einer weniger als optimalen Darstellung.
Der erste Code-Snippet sollte zwei Doppelpunkte nach dem :empty-Selektor haben. So
div:empty::after
Beide sind gültige Syntaxen
https://developer.mozilla.org/en-US/docs/Web/CSS/::after#Syntax