CSS wird immer leistungsfähiger, und mit Funktionen wie CSS Grid und benutzerdefinierten Eigenschaften (auch bekannt als CSS-Variablen) sehen wir einige wirklich kreative Lösungen. Einige dieser Lösungen konzentrieren sich nicht nur darauf, das Web schöner zu machen, sondern es zugänglicher zu machen und das Styling von zugänglichen Erlebnissen zu verbessern. Ich bin definitiv dafür!
Artikelserie
- SVG-Hintergründe einfärben
- Dropdown-Menüs (dieser Beitrag)
- Logisches Styling basierend auf der Anzahl der gegebenen Elemente
Ein gängiges UI-Muster, das wir im Web sehen, sind Dropdown-Menüs. Sie werden verwendet, um verwandte Informationen stückweise anzuzeigen, ohne den Benutzer mit Buttons, Text und Optionen zu überfordern. Wir sehen diese oft in Headern oder Navigationsbereichen von Websites.

Mal sehen, ob wir eines dieser Menüs nur mit CSS erstellen können. Wir erstellen eine Liste von Links innerhalb einer Nav-Komponente wie folgt
<nav role="navigation">
<ul>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
<li><a href="#">Three</a></li>
</ul>
</nav>
Nun wollen wir ein Dropdown-Untermenü auf dem zweiten Navigationspunkt haben. Wir können dort dasselbe tun und eine Liste von Links in diesem Listenelement aufnehmen
<nav role="navigation">
<ul>
<li><a href="#">One</a></li>
<li><a href="#">Two</a>
<ul class="dropdown">
<li><a href="#">Sub-1</a></li>
<li><a href="#">Sub-2</a></li>
<li><a href="#">Sub-3</a></li>
</ul>
</li>
<li><a href="#">Three</a></li>
</ul>
</nav>
Wir haben nun unser zweistufiges Navigationssystem. Um den Inhalt versteckt und angezeigt zu haben, wenn wir ihn sichtbar machen wollen, müssen wir etwas CSS anwenden. Alle Stileigenschaften wurden aus dem folgenden Beispiel zur Klarheit der Interaktion entfernt
li {
display: block;
transition-duration: 0.5s;
}
li:hover {
cursor: pointer;
}
ul li ul {
visibility: hidden;
opacity: 0;
position: absolute;
transition: all 0.5s ease;
margin-top: 1rem;
left: 0;
display: none;
}
ul li:hover > ul,
ul li ul:hover {
visibility: visible;
opacity: 1;
display: block;
}
ul li ul li {
clear: both;
width: 100%;
}
Nun ist das Untermenü-Dropdown versteckt, wird aber angezeigt und sichtbar, wenn wir mit der Maus über das entsprechende übergeordnete Element in der Navigationsleiste fahren. Durch das Stylen von ul li ul haben wir Zugriff auf dieses Untermenü, und durch das Stylen von ul li ul li haben wir Zugriff auf die einzelnen Listenelemente darin.
Das Problem
Das sieht schon mal so aus, wie wir es uns wünschen, aber wir sind noch lange nicht fertig. Web-Barrierefreiheit ist ein Kernbestandteil der Entwicklung Ihres Produkts, und jetzt wäre der perfekte Zeitpunkt, dies anzusprechen. Das Hinzufügen von role="navigation" ist ein guter Anfang, aber damit eine Navigationsleiste barrierefrei ist, sollte man sie mit der Tabulatortaste durchgehen können (und das richtige Element in einer sinnvollen Reihenfolge fokussieren) und auch einen Screenreader genau vorlesen lassen, was gerade fokussiert wird.
Sie können mit der Maus über jedes der Listenelemente fahren und deutlich sehen, worüber gefahren wird, aber das gilt nicht für die Tabulator-Navigation. Probieren Sie es aus, und tabben Sie durch das obige Beispiel. Sie verlieren die Übersicht, wo der Fokus visuell ist. Wenn Sie zu **Zwei** im Hauptmenü tabben, sehen Sie einen Fokusindikatorring, aber wenn Sie zum nächsten Element (einem seiner Untermenü-Elemente) tabben, verschwindet dieser Fokus.

Es ist wichtig zu beachten, dass Sie theoretisch auf dieses andere Element fokussiert sind und ein Screenreader dies verarbeiten und **Unter-Eins** vorlesen könnte, aber Tastaturbenutzer werden nicht sehen können, was vor sich geht, und werden die Übersicht verlieren.
Der Grund dafür ist, dass wir beim Stylen des Hover-Zustands des übergeordneten Elements, sobald wir den Fokus vom übergeordneten Element auf eines der Listenelemente innerhalb dieses übergeordneten Elements übertragen, diese Stile verlieren. Das ist aus CSS-Sicht sinnvoll, aber nicht das, was wir wollen.
Glücklicherweise gibt es eine neue CSS-Pseudoklasse, die uns genau das gibt, was wir in diesem Fall wollen, und sie heißt :focus-within.
Die Lösung: :focus-within
Der :focus-within-Pseudoselektor ist Teil des CSS Selectors Level 4 Spec und weist den Browser an, einen Stil auf ein übergeordnetes Element anzuwenden, wenn eines seiner untergeordneten Elemente fokussiert ist. In unserem Fall bedeutet dies, dass wir zu **Unter-Eins** tabben und einen :focus-within-Stil zusammen mit dem :hover-Stil des übergeordneten Elements anwenden können und genau sehen, wo wir uns im Dropdown-Menü befinden. In unserem Fall wäre das ul li:focus-within > ul
ul li:hover > ul,
ul li:focus-within > ul,
ul li ul:hover {
visibility: visible;
opacity: 1;
display: block;
}
Super! Es funktioniert!
Kurzer Umweg! Wenn Sie nur moderne Browser unterstützen, sind die bisherigen CSS-Angaben in Ordnung. Aber Sie sollten wissen, dass, wenn *irgendein* Browser einen Teil eines Selektors nicht versteht, er den gesamten Selektor verwirft. Wenn Sie also IE 11 unterstützen möchten, können Sie den Teil :focus-within nicht mischen.
/* This compound selector will still work in IE 11 because :focus-within isn't mixed in */
ul li:hover > ul,
ul li ul:hover,
ul li ul:focus {
visibility: visible;
opacity: 1;
display: block;
}
/* IE 11 won't get this, but at least the top-level menus will work */
ul li:focus-within > ul {
visibility: visible;
opacity: 1;
display: block;
}
Jetzt, wenn wir zum zweiten Element tabben, erscheint unser Untermenü, und wenn wir durch das Untermenü tabben, bleibt die Sichtbarkeit erhalten! Nun können wir unseren Code erweitern, um :focus-Zustände neben :hover aufzunehmen, um Tastaturnutzern das gleiche Erlebnis wie unseren Mausbenutzern zu bieten.

In den meisten Fällen, wie bei direkten Links, können wir normalerweise einfach etwas wie das schreiben
a:hover,
a:focus {
...
}
Aber in diesem Fall, da wir Hover-Stile basierend auf dem übergeordneten li anwenden, können wir wieder :focus-within verwenden, um das gleiche Aussehen und Gefühl beim Tabben zu erzielen. Das liegt daran, dass wir das li nicht tatsächlich *fokussieren* können (es sei denn, wir fügen einen tabindex="0" hinzu). Wir fokussieren tatsächlich den Link (a) darin. :focus-within erlaubt es uns immer noch, Stile auf das übergeordnete li anzuwenden, wenn der Link fokussiert ist (ziemlich cool!)
li:hover,
li:focus-within {
...
}

Zu diesem Zeitpunkt können wir, da wir einen Fokus-Stil anwenden, etwas tun, das typischerweise nicht empfohlen wird (die Hervorhebung dieses blauen Fokusrings entfernen). Das können wir tun, indem wir
li:focus-within a {
outline: none;
}
Der obige Code gibt an, dass beim Fokussieren innerhalb von Listenelementen über den Link (a) kein Umriss auf das Linkelement (a) angewendet werden soll. Es ist ziemlich sicher, dies so zu schreiben, da wir ausschließlich den Hover-Zustand stylen, und bei Browsern, die :focus-within nicht unterstützen, erhält der Link immer noch einen Fokusring. Nun sieht unser Menü so aus

:focus-within, :hover-Zuständen und Anpassung des Fokusrings zum VerschwindenWas ist mit ARIA?
Wenn Sie mit Barrierefreiheit vertraut sind, haben Sie vielleicht von ARIA-Labels und -Status gehört. Sie können diese auch zu Ihrem Vorteil nutzen, um diese Arten von Dropdowns mit integrierter Barrierefreiheit gleichzeitig zu erstellen! Ein ausgezeichnetes Beispiel finden Sie hier von Heydon Pickering. Beim Einbeziehen von ARIA-Markup würde Ihr Code eher so aussehen
<nav role="navigation">
<ul>
<li><a href="#">One</a></li>
<li><a href="#" aria-haspopup="true">Two</a>
<ul class="dropdown" aria-label="submenu">
<li><a href="#">Sub-1</a></li>
<li><a href="#">Sub-2</a></li>
<li><a href="#">Sub-3</a></li>
</ul>
</li>
<li><a href="#">Three</a></li>
</ul>
</nav>
Sie fügen aria-haspopup="true" zum übergeordneten Element des Dropdown-Menüs hinzu, um einen alternativen Status anzuzeigen, und schließen aria-label="submenu" in das eigentliche Dropdown-Menü ein (in diesem Fall unsere Liste mit der class="dropdown").
Diese Eigenschaften selbst geben Ihnen die benötigte Funktionalität, um das Dropdown-Menü anzuzeigen, aber der Nachteil ist, dass sie nur mit aktiviertem JavaScript funktionieren.
Hinweis zur Browserunterstützung
Wo wir gerade bei Hinweisen sind, reden wir über die Browserunterstützung. Obwohl :focus-within *ziemlich gute* Browserunterstützung hat, ist es wichtig zu beachten, dass Internet Explorer und Edge nicht unterstützt werden, sodass Ihre Benutzer auf diesen Plattformen die Navigation nicht sehen können.
Diese Daten zur Browserunterstützung stammen von Caniuse, wo es weitere Details gibt. Eine Zahl zeigt an, dass der Browser die Funktion ab dieser Version unterstützt.
Desktop
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 60 | 52 | Nein | 79 | 10.1 |
Mobil / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 127 | 127 | 127 | 10.3 |
Die ultimative Lösung wäre hier die Verwendung von *sowohl* ARIA-Markup *als auch* CSS :focus-within, um ein robustes Dropdown-Erlebnis für Ihre Benutzer zu gewährleisten.
Wenn Sie diese Funktion in Zukunft nutzen möchten, stimmen Sie dafür auf Edge User Voice ab! Und stimmen Sie auch für :focus-ring, damit wir diesen Fokusring gestalten und ein schönes interaktives Web-Erlebnis für alle schaffen können 😀
Mehr über :focus-within und A11Y
- Scott O’Hara schrieb über
:focus-withinund hob Demos hervor, wie z.B. hervorgehobene<table>-Zeilen und Dropdown-Menüs - Kushagra Gour zum Thema Erstellung eines modalen Fensters mit gefangenem Fokus mit CSS
- Eric Bailey zu Fokusstilen im Allgemeinen
- Chris über das Sichtbarmachen eines übergeordneten Elements, wenn ein untergeordnetes Element fokussiert ist
- Alle Artikel auf CSS-Tricks zum Thema
:focus-within
Es gibt ein
:focus-withinPolyfill https://github.com/jonathantneal/focus-withinEs funktioniert wie das
:focus-visiblePolyfill und erfordert, dass Sie ein Fallback-Attribut oder einen Klassennamen verwenden. In der Zwischenzeit können Sie es auf die CSS-Standardweise schreiben und den Fallback-Selektor mit https://github.com/jonathantneal/postcss-focus-within automatisch hinzufügen lassen.Hey, toller Artikel!
Beim Herausbewegen der Maus aus einem Untermenü und dann wieder Hineinbewegen in diesen Bereich macht das Untermenü wieder sichtbar, ohne dass das übergeordnete Element berührt werden muss. Das kann bei vielen Untermenüs und tieferer Verschachtelung etwas lästig sein.
Ich würde
display: none;zuul li ulunddisplay: block;zuul li:hover > ulhinzufügen, um das zu beheben.Guter Punkt! Ich habe das in den Codepen-Beispielen hinzugefügt
Sie könnten auch
pointer-events: none;auful li ulund dannpointer-events: all;auful li:hover > ulanwenden, um zu verhindern, dassdisplay: none;die Übergänge beeinflusst, die Sie für die Untermenüs verwenden.Hallo, toller Artikel mit guten Tipps. Würden Sie vielleicht eine Verzögerung für den Übergang hinzufügen, bevor das Menü erscheint? Falls Sie nicht mit dem Menü interagieren möchten, aber Ihr Cursor darüber schwebt, erscheint es nicht, was in Ihrem Beispiel nicht der Fall ist.
Das funktioniert bei mir in Firefox nicht. In Chrome habe ich es geöffnet und es ist in Ordnung. Ich genieße diese Tutorials und diese Seite sehr. Vielen Dank fürs Teilen.
Großartig! Würde die Zugänglichkeit durch die Verwendung von aria-expanded erhöht?
Vielen Dank für die Einbeziehung von Barrierefreiheit! Eine Sache, die für Tastaturnutzer mühsam ist, ist jedoch, dass man durch alle Untermenü-Elemente getabbt wird. Typischerweise für größere Mega-Menüs muss man mehr Arbeit für eine gute UX leisten: entweder das Ganze als einen einzigen Tabulatorstopp behandeln und die Pfeiltasten verwenden oder nur die obersten Elemente tabulatorisch machen. Die Unterelemente können dann mit TAB übersprungen, aber mit Enter zum Aktivieren eines Untermenüs und mit den Pfeiltasten erreicht werden, wenn es geöffnet ist (Target.com macht das so). Es hilft der Tastaturproduktivität, nicht durch alle Unterelemente gezwungen zu werden.
Guter Punkt, Marcy. Gibt es dafür ein Codebeispiel, das an die obigen Beispielcodes gebunden ist?
Eine weitere Option, die in den WCAG-Spezifikationen aufgeführt ist, ist die Bereitstellung eines "Skip to Content"-Links. Wir stellen ihn so ein, dass er nur für Benutzer sichtbar ist, die mit der Tastatur navigieren, als erstes Element in der Tabulatorreihenfolge.
Hier ist ein Artikel und Codepen-Beispiel, das ich gerade auf meiner Website dazu veröffentlicht habe und das Marcs Plan verwendet.
Was zum Teufel ist mit
menuundcommand?Großartig! Das ist, was ich in dem Kommentar meine: https://css-tricks.de/keeping-parent-visible-child-focus/#comment-1613750
Die Farbwahl des übergeordneten Menüs beim Hover beizubehalten ist so einfach wie die Kombination von
nav li a:focus, nav li a:hoverundnav li:hover > a. Aber die Untermenüs nach dem Verlust des Fokus ohne JavaScript sichtbar zu halten, ist eine andere Sache.Erstaunlicher Artikel mit prägnanten Beispielen und, was am wichtigsten ist, lesbarem Code.
Eine kleine Anmerkung, aber.
Nach meinem Wissen müssen Sie
role="navigation"nicht zum<nav>-HTMLElement hinzufügen, da dieses selbst einen Navigationslandmark definiert (ohne jederole).Von https://www.w3.org/TR/wai-aria-practices/#aria_lh_navigation
Eine Sache, die ich an Ihrem CSS nicht verstehe, ist, warum Sie
clear: bothauful li ul lihinzugefügt haben, Sie scheinen keine Floats zu verwenden?Für bessere Browserunterstützung könnten Sie anstelle von :focus-within Folgendes tun:
[code]a:focus + ul {
…
}[/code]
Schreiben Sie nicht li:hover, li:focus-within. IE unterstützt :focus-within nicht. Aus diesem Grund funktioniert :hover nicht. Es ist besser, li:hover{/*styles*/} li:focus-within{/*styles*/} zu verwenden.
Es scheint, dass Heydon seine Empfehlungen für Menü-/Navigationskomponenten und generell für Barrierefreiheit auf dem neuesten Stand gehalten hat. Im Falle einer normalen Website-Navigation schlägt er vor, das Attribut aria-haspopup nicht zu verwenden.
https://inclusive-components.design/menus-menu-buttons/
Außerdem ist die Angabe von role="navigation" für das Nav-Element wahrscheinlich nicht notwendig, da es vom Browser implizit gesetzt wird.
Machen Sie weiter so!
Das Dropdown funktioniert auf iOS 9.3.5 in Safari oder Chrome nicht. Ich habe kein neueres iOS zum Testen. Perfekt in Chrome auf dem neuesten Android, einschließlich Tippen irgendwo zum Schließen.
Nette Tricks, Una, ich sehe einige Möglichkeiten für Mikrointeraktionen (schwebende Labels für Eingabefelder sind mir eingefallen)
Für eine so zentrale Komponente wie die Navigation, aufgrund der Browserunterstützung und wie von @marcysutton beim Punkt Tastaturzugänglichkeit erwähnt, glaube ich nicht, dass dies der beste Fluss ist und er wird von W3C nicht empfohlen.
Danke für das Teilen.
Leider funktioniert es nicht ideal beim Zurück-Tabben (Shift + Tab), da die Tabulator-Reihenfolge die Unterelemente "überspringt". Für die Barrierefreiheit wird erwartet, dass die Tabulator-Reihenfolge in beide Richtungen gleich ist.