Mit CSS gelöst! Dropdown-Menüs

Avatar of Una Kravets
Una Kravets am

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

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

  1. SVG-Hintergründe einfärben
  2. Dropdown-Menüs (dieser Beitrag)
  3. 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.

A collage of screenshots showing different dropdown menu examples.
Eine Google-Suche nach "Dropdown-Menü" liefert viele Beispiele

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.

An animated screenshot showing focus rings on menu items as they are tabbed.

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.

An animated screenshot of a menu showing the sybmenu being revealed when it is actively tabbed.

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 {
  ...
}
An animated screenshot showing a tabbed menu where the submenu is revealed when actively tabbed, the submenu items show the focus ring when active, and the hover styles are also applied when active.

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

An animated screenshot showing the final result of the tabbed menu where the focus ring has been removed and replaced by the hover state when menu items are actively tabbed.
Finales Menü mit :focus-within, :hover-Zuständen und Anpassung des Fokusrings zum Verschwinden

Was 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

ChromeFirefoxIEEdgeSafari
6052Nein7910.1

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712710.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