Die Balance zwischen nativen und benutzerdefinierten Select-Elementen finden

Avatar of Sandrina Pereira
Sandrina Pereira am

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

Hier ist der Plan! Wir werden ein gestyltes Select-Element erstellen. Nicht nur die Außenseite, sondern auch die Innenseite. Totale Styling-Kontrolle. Außerdem machen wir es zugänglich. Wir werden nicht versuchen, alles zu replizieren, was der Browser standardmäßig mit einem nativen <select>-Element tut. Wir werden buchstäblich ein <select>-Element verwenden, wenn assistierende Technologien genutzt werden. Aber wenn eine Maus verwendet wird, zeigen wir die gestylte Version an und lassen sie als Select-Element funktionieren.

Das ist es, was ich mit "hybriden" Selects meine: Sie sind sowohl ein natives <select> als auch ein gestylter alternativer Select in einem Designmuster.

Benutzerdefinierte Selects (links) werden oft anstelle von nativen Selects (rechts) aus ästhetischen Gründen und für Designkonsistenz verwendet.

Select, Dropdown, Navigation, Menü ... der Name zählt

Bei der Recherche für diesen Artikel habe ich über viele Namen nachgedacht, die beim Sprechen über Selects verwendet werden, die häufigsten davon sind "Dropdown" und "Menü". Es gibt zwei Arten von Namensfehlern, die wir machen könnten: Dinge mit dem gleichen Namen benennen oder das Gleiche mit unterschiedlichen Namen benennen. Ein Select kann unter beidem leiden.

Bevor wir weitermachen, versuche ich, die Verwendung des Begriffs "Dropdown" zu verdeutlichen. Hier definiere ich die Bedeutung von Dropdown

Dropdown: Eine interaktive Komponente, die aus einem Button besteht, der eine Liste von Elementen anzeigt und ausblendet, typischerweise beim Mouse-Hover, Klicken oder Tippen. Die Liste ist standardmäßig nicht sichtbar, bis die Interaktion beginnt. Die Liste zeigt normalerweise einen Block von Inhalten (z. B. Optionen) über anderen Inhalten an.

Viele Benutzeroberflächen können wie ein Dropdown aussehen. Aber ein Element einfach "Dropdown" zu nennen, ist wie "Fisch" zu verwenden, um ein Tier zu beschreiben. Welche Art von Fisch ist es? Ein Clownfisch ist nicht dasselbe wie ein Hai. Dasselbe gilt für Dropdowns.

So wie es verschiedene Fischarten im Meer gibt, gibt es verschiedene Arten von Komponenten, über die wir sprechen könnten, wenn wir das Wort "Dropdown" verwenden

  • Menü: Eine Liste von Befehlen oder Aktionen, die der Benutzer innerhalb des Seiteninhalts ausführen kann.
  • Navigation: Eine Liste von Links, die zur Navigation durch eine Website verwendet wird.
  • Select: Eine Formularsteuerung (<select>), die eine Liste von Optionen anzeigt, aus denen der Benutzer in einem Formular auswählen kann.

Zu entscheiden, um welche Art von Dropdown es sich handelt, kann eine neblige Aufgabe sein. Hier sind einige Beispiele aus dem Web, die meinen Klassifizierungen dieser drei verschiedenen Typen entsprechen. Dies basiert auf meiner Recherche und manchmal, wenn ich keine richtige Antwort finde, auf Intuition basierend auf meiner Erfahrung.

Dropdown-Land: Fünf Szenarien, in denen unterschiedliche Dropdowns im Internet verwendet werden. Lesen Sie die folgende Tabelle für eine detaillierte Beschreibung.
DiagrammbezeichnungSzenarioDropdown-Typ
1Das Dropdown erwartet, dass eine ausgewählte Option im Kontext eines Formulars übermittelt wird (z. B. Alter auswählen)Select
2Das Dropdown benötigt keine aktive Option (z. B. Eine Liste von Aktionen: Kopieren, Einfügen und Ausschneiden)Menü
3Die ausgewählte Option beeinflusst den Inhalt. (z. B. Sortierliste)Menü oder Select (mehr dazu später)
4Das Dropdown enthält Links zu anderen Seiten. (z. B. Ein "Meganav" mit Website-Links)Offenlegungsnavigation
5Das Dropdown enthält Inhalte, die keine Liste sind. (z. B. ein Datumsauswahl)Etwas anderes, das nicht als Dropdown bezeichnet werden sollte

Nicht jeder nimmt das Internet auf die gleiche Weise wahr und interagiert damit. Das Benennen von Benutzeroberflächen und das Definieren von Designmustern ist ein grundlegender Prozess, wenn auch mit viel Raum für persönliche Interpretation. All diese Variationen treiben die Population des Dropdown-Landes an. 

Es gibt einen Dropdown-Typ, der eindeutig ein Menü ist. Seine Verwendung ist ein heißes Thema in Gesprächen über Barrierefreiheit. Ich werde hier nicht viel darüber sprechen, aber lassen Sie mich nur bekräftigen, dass das <menu>-Element veraltet ist und nicht mehr empfohlen wird. Und hier ist eine detaillierte Erklärung zu inklusiven Menüs und Menüschaltflächen, einschließlich warum die ARIA-Menürolle nicht für die Website-Navigation verwendet werden sollte.

Wir haben noch nicht einmal andere Elemente angesprochen, die in einen eher grauen Bereich fallen, der die Klassifizierung von Dropdowns aufgrund mangelnder praktischer Anwendungsfälle aus der WCAG-Community noch unklarer macht.

Uff ... das war viel. Vergessen wir dieses Dropdown-Land-Chaos und konzentrieren uns ausschließlich auf den Dropdown-Typ, der eindeutig ein <select>-Element ist.

Sprechen wir über <select>

Das Styling von Formularsteuerelementen ist eine interessante Reise. Wie MDN sagt, gibt es das Gute, das Schlechte und das Hässliche. Gut sind Dinge wie <form>, das nur ein blockbasiertes Element zum Stylen ist. Schlecht sind Dinge wie Kontrollkästchen, die gemacht werden können, aber etwas umständlich sind. <select> ist definitiv in hässlichem Terrain.

Es wurden viele Artikel darüber geschrieben, und selbst im Jahr 2020 ist es immer noch eine Herausforderung, benutzerdefinierte Selects zu erstellen und einige Benutzer bevorzugen immer noch die einfachen nativen

Unter Entwicklern ist <select> bei weitem die frustrierendste Formularsteuerung, hauptsächlich wegen ihrer mangelnden Styling-Unterstützung. Der UX-Kampf dahinter ist so groß, dass wir nach anderen Alternativen suchen. Nun, ich schätze, die erste Regel für <select> ist ähnlich wie bei ARIA: Vermeiden Sie die Verwendung, wenn Sie können.

Ich könnte den Artikel hier beenden mit "Verwenden Sie <select> nicht, Punkt." Aber lassen Sie uns der Realität ins Auge sehen: Ein Select ist in einer Reihe von Umständen immer noch unsere beste Lösung. Das können Szenarien sein, in denen wir mit einer Liste mit vielen Optionen arbeiten, in Layouts, die wenig Platz haben, oder einfach an mangelnder Zeit oder Budget, um eine großartige benutzerdefinierte interaktive Komponente von Grund auf zu entwerfen und zu implementieren.

Anforderungen an benutzerdefinierte <select>-Elemente

Wenn wir die Entscheidung treffen, ein benutzerdefiniertes Select zu erstellen - selbst wenn es nur ein "einfaches" ist - sind dies die Anforderungen, mit denen wir im Allgemeinen arbeiten müssen

  • Es gibt einen Button, der die aktuell ausgewählte Option enthält.
  • Das Klicken auf das Feld schaltet die Sichtbarkeit der Optionsliste (auch Listbox genannt) um.
  • Das Klicken auf eine Option in der Listbox aktualisiert den ausgewählten Wert. Der Button-Text ändert sich und die Listbox wird geschlossen.
  • Das Klicken außerhalb der Komponente schließt die Listbox.
  • Der Trigger enthält ein kleines Dreieck, das nach unten zeigt, um anzuzeigen, dass Optionen vorhanden sind.

Etwas wie das

Manche von Ihnen denken vielleicht, dass das funktioniert und gut ist. Aber warten Sie ... funktioniert das für jeden? Nicht jeder benutzt eine Maus (oder einen Touchscreen). Außerdem bietet ein natives <select>-Element mehr Funktionen, die wir kostenlos erhalten und die nicht in diesen Anforderungen enthalten sind, wie z. B.

  • Die ausgewählte Option ist für alle Benutzer wahrnehmbar, unabhängig von ihren visuellen Fähigkeiten.
  • Die Komponente kann auf vorhersehbare Weise über alle Browser mit einer Tastatur interagieren (z. B. Pfeiltasten zur Navigation, Enter zur Auswahl, Esc zum Abbrechen usw.).
  • Assistive Technologien (z. B. Screenreader) kündigen das Element klar an, einschließlich seiner Rolle, seines Namens und seines Zustands.
  • Die Position der Listbox wird angepasst. (d. h. sie wird nicht vom Bildschirm abgeschnitten).
  • Das Element respektiert die Einstellungen des Betriebssystems des Benutzers (z. B. hoher Kontrast, Farbschema, Bewegung usw.).

Hier scheitern die meisten benutzerdefinierten Selects auf irgendeine Weise. Werfen Sie einen Blick auf einige der großen UI-Komponentenbibliotheken. Ich werde keine nennen, da das Web vergänglich ist, aber versuchen Sie es. Sie werden wahrscheinlich feststellen, dass sich die Select-Komponente in einem Framework anders verhält als in einem anderen. 

Hier sind zusätzliche Merkmale, auf die Sie achten sollten

  • Wird eine Listbox-Option sofort bei Fokus aktiviert, wenn mit der Tastatur navigiert wird?
  • Kann man Enter und/oder Space verwenden, um eine Option auszuwählen?
  • Springt die Tab-Taste zur nächsten Option in der Listbox oder zur nächsten Formularsteuerung?
  • Was passiert, wenn man mit den Pfeiltasten die letzte Option in der Listbox erreicht? Bleibt sie einfach auf dem letzten Element, springt sie zurück zur ersten Option oder, schlimmer noch, wechselt der Fokus zur nächsten Formularsteuerung? 
  • Ist es möglich, mit der Page Down-Taste direkt zum letzten Element in der Listbox zu springen?
  • Ist es möglich, durch die Listbox-Elemente zu scrollen, wenn mehr vorhanden sind, als aktuell sichtbar sind?

Dies ist eine kleine Auswahl an Funktionen, die in einem nativen <select>-Element enthalten sind.

Sobald wir uns entscheiden, unser eigenes benutzerdefiniertes Select zu erstellen, zwingen wir die Leute, es auf eine bestimmte Weise zu verwenden, die möglicherweise nicht ihren Erwartungen entspricht.

Aber es wird noch schlimmer. Selbst das native <select> verhält sich unterschiedlich zwischen Browsern und Screenreadern. Sobald wir uns entscheiden, unser eigenes benutzerdefiniertes Select zu erstellen, zwingen wir die Leute, es auf eine bestimmte Weise zu verwenden, die möglicherweise nicht ihren Erwartungen entspricht. Das ist eine gefährliche Entscheidung und in diesen Details steckt der Teufel.

Erstellung eines "hybriden" Selects

Wenn wir einen einfachen benutzerdefinierten Select erstellen, treffen wir einen Kompromiss, ohne es zu merken. Insbesondere opfern wir Funktionalität für Ästhetik. Es sollte umgekehrt sein.

Was wäre, wenn wir stattdessen standardmäßig einen nativen Select liefern und ihn durch einen ästhetisch ansprechenderen ersetzen, wenn möglich? Hier kommt die Idee des "hybriden" Selects ins Spiel. Er ist "hybrid", weil er aus zwei Selects besteht und den entsprechenden im richtigen Moment anzeigt

  • Ein natives Select, standardmäßig sichtbar und zugänglich
  • Ein benutzerdefiniertes Select, versteckt, bis es sicher ist, mit einer Maus damit zu interagieren

Beginnen wir mit der Markup. Zuerst fügen wir ein natives <select> mit <option>-Elementen vor dem benutzerdefinierten Selektor hinzu, damit dies funktioniert. (Ich werde gleich erklären, warum.)

Jede Formularsteuerung muss ein beschreibendes Label haben. Wir könnten <label> verwenden, aber das würde den nativen Select fokussieren, wenn auf das Label geklickt wird. Um dieses Verhalten zu verhindern, verwenden wir ein <span> und verbinden es mit dem Select über aria-labelledby.

Schließlich müssen wir assistierende Technologien mitteilen, den benutzerdefinierten Select zu ignorieren, indem wir aria-hidden="true" verwenden. So wird unabhängig davon nur der native Select von ihnen angesagt.

<span class="selectLabel" id="jobLabel">Main job role</span>
<div class="selectWrapper">
  <select class="selectNative js-selectNative" aria-labelledby="jobLabel">
    <!-- options -->
    <option></option>
  </select>
  <div class="selectCustom js-selectCustom" aria-hidden="true">
     <!-- The beautiful custom select -->
  </div>
</div>

Das bringt uns zum Styling, wo wir nicht nur Dinge schön machen, sondern auch den Wechsel von einem Select zum anderen handhaben. Wir brauchen nur ein paar neue Deklarationen, um die Magie geschehen zu lassen.

Erstens müssen sowohl native als auch benutzerdefinierte Selects die gleiche Breite und Höhe haben. Dies stellt sicher, dass die Leute beim Wechsel keine großen Unterschiede im Layout sehen.

.selectNative,
.selectCustom {
  position: relative;
  width: 22rem;
  height: 4rem;
}

Es gibt zwei Selects, aber nur einer kann den Platz bestimmen, der sie enthält. Der andere muss absolut positioniert werden, um ihn aus dem Dokumentfluss zu nehmen. Das machen wir mit dem benutzerdefinierten Select, da er der "Ersatz" ist, der nur verwendet wird, wenn er möglich ist. Wir verstecken ihn standardmäßig, damit er noch von niemandem erreicht werden kann.

.selectCustom {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
}

Jetzt kommt der "lustige" Teil. Wir müssen erkennen, ob jemand ein Gerät benutzt, auf dem Hover Teil der primären Eingabe ist, wie z. B. ein Computer mit einer Maus. Während wir normalerweise an Media Queries für reaktionsfähige Breakpoints oder das Prüfen der Feature-Unterstützung denken, können wir sie auch verwenden, um Hover-Unterstützung zu erkennen, indem wir @media query (hover :hover) verwenden, das von allen wichtigen Browsern unterstützt wird. Also, wir verwenden es, um den benutzerdefinierten Select nur auf Geräten anzuzeigen, die Hover haben

@media (hover: hover) {
  .selectCustom {
    display: block;
  }
}

Großartig, aber was ist mit Menschen, die eine Tastatur zur Navigation verwenden, auch auf Geräten, die Hover haben? Wir werden den benutzerdefinierten Select verstecken, wenn der native Select im Fokus ist. Wir können einen Kombinator für benachbarte Geschwister (+) verwenden. Wenn der native Select im Fokus ist, verstecken Sie den benutzerdefinierten Select, der sich daneben im DOM-Reihenfolge befindet. (Deshalb sollte der native Select vor dem benutzerdefinierten platziert werden.)

@media (hover: hover) {
  .selectNative:focus + .selectCustom {
    display: none;
  }
}

Das war's! Der Trick, zwischen beiden Selects zu wechseln, ist erledigt! Es gibt natürlich andere CSS-Wege, dies zu tun, aber dieser funktioniert gut.

Zuletzt brauchen wir ein bisschen JavaScript. Fügen wir einige Event-Listener hinzu

  • Einer für Klickereignisse, der den benutzerdefinierten Select zum Öffnen und Anzeigen der Optionen auslöst
  • Einer, um die Werte beider Selects zu synchronisieren. Wenn sich der Wert eines Selects ändert, wird auch der Wert des anderen Selects aktualisiert.
  • Einer für grundlegende Tastaturnavigationssteuerungen, wie Navigation mit den Up- und Down-Tasten, Auswahl von Optionen mit den Enter- oder Space-Tasten und Schließen des Select mit Esc

Usability-Tests

Ich habe einen sehr kleinen Usability-Test durchgeführt, bei dem ich einige Personen mit Behinderungen gebeten habe, die hybride Select-Komponente auszuprobieren. Die folgenden Geräte und Werkzeuge wurden mit den neuesten Versionen von Chrome (81), Firefox (76) und Safari (13) getestet

  • Desktop-Gerät nur mit Maus
  • Desktop-Gerät nur mit Tastatur
  • VoiceOver unter MacOS mit Tastatur
  • NVDA unter Windows mit Tastatur
  • VoiceOver auf iPhone und iPad mit Safari

Alle diese Tests funktionierten wie erwartet, aber ich glaube, dies könnte noch mehr Usability-Tests mit vielfältigeren Personen und Werkzeugen benötigen. Wenn Sie Zugang zu anderen Geräten oder Werkzeugen haben – wie JAWS, Dragon usw. – teilen Sie mir bitte mit, wie der Test verläuft.

Während der Tests wurde ein Problem gefunden. Insbesondere gab es ein Problem mit der VoiceOver-Einstellung "Mauszeiger: Bewegt Voice Over Cursor". Wenn der Benutzer das Select mit der Maus öffnet, wird das benutzerdefinierte Select geöffnet (anstelle des nativen) und der Benutzer erlebt das native Select nicht.

Was mir an diesem Ansatz am besten gefällt, ist, wie er das Beste aus beiden Welten nutzt, ohne die Kernfunktionalität zu beeinträchtigen

  • Benutzer auf Mobilgeräten und Tablets erhalten das native Select, das im Allgemeinen eine bessere Benutzererfahrung bietet als ein benutzerdefiniertes Select, einschließlich Leistungsvorteilen.
  • Tastaturbedienende Benutzer können auf die erwartete Weise mit dem nativen Select interagieren.
  • Assistive Technologies können normal mit dem nativen Select interagieren.
  • Mausbenutzer können mit dem erweiterten benutzerdefinierten Select interagieren.

Dieser Ansatz liefert wesentliche native Funktionalität für alle, ohne den zusätzlichen enormen Codeaufwand, um alle nativen Funktionen zu implementieren.

Verstehen Sie mich nicht falsch. Diese Technik ist keine Einheitslösung. Sie funktioniert möglicherweise für einfache Selects, aber wahrscheinlich nicht für Fälle mit komplexen Interaktionen. In diesen Fällen müssten wir ARIA und JavaScript verwenden, um die Lücken zu ergänzen und ein wirklich zugängliches benutzerdefiniertes Select zu erstellen.

Ein Hinweis zu Selects, die wie Menüs aussehen

Werfen wir einen Blick zurück auf das dritte Szenario im Dropdown-Land. Wenn Sie sich erinnern, ist es ein Dropdown, das immer eine ausgewählte Option hat (z. B. Sortieren von Inhalten). Ich habe es in den grauen Bereich eingeordnet, entweder als Menü oder als Select. 

Hier ist meine Denkweise: Vor Jahren wurde diese Art von Dropdown meist über ein natives <select> implementiert. Heutzutage ist es üblich, es von Grund auf mit benutzerdefinierten Stilen (zugänglich oder nicht) zu implementieren. Was wir am Ende haben, ist ein Select-Element, das wie ein Menü aussieht. 

Three similar dropdowns that always have a selected option.

Ein <select> ist eine Art Menü. Beide haben ähnliche Semantik und Verhalten, insbesondere in einem Szenario, das eine Liste von Optionen beinhaltet, bei der immer eine ausgewählt ist. Nun, lassen Sie mich das WCAG 3.2.2 On Input (Level A)-Kriterium erwähnen

Die Änderung der Einstellung einer Benutzeroberflächenkomponente darf nicht automatisch zu einem Kontextwechsel führen, es sei denn, der Benutzer wurde vor der Verwendung der Komponente über das Verhalten informiert.

Lassen Sie uns das in die Praxis umsetzen. Stellen Sie sich eine sortierbare Liste von Studenten vor. Visuell mag es offensichtlich sein, dass das Sortieren sofort erfolgt, aber das ist nicht unbedingt für jeden wahr. Wenn wir also <select> verwenden, laufen wir Gefahr, die WCAG-Richtlinie zu verletzen, da sich der Seiteninhalt geändert hat, und das signifikante Neuordnen des Seiteninhalts gilt als Kontextwechsel.

Um die Erfüllung des Kriteriums sicherzustellen, müssen wir den Benutzer vor der Interaktion mit dem Element warnen oder direkt nach dem Select einen <button> einfügen, um die Änderung zu bestätigen.

<label for="sortStudents">
  Sort students
  <!-- Warn the user about the change when a confirmation button is not present. -->
  <span class="visually-hidden">(Immediate effect upon selection)</span>
</label>
<select id="sortStudents"> ... </select>

Das gesagt, ist die Verwendung eines <select> oder die Erstellung eines benutzerdefinierten Menüs beides gute Ansätze, wenn es um einfache Menüs geht, die den Seiteninhalt ändern. Denken Sie nur daran, dass Ihre Entscheidung den Aufwand bestimmt, der erforderlich ist, um die Komponente vollständig zugänglich zu machen. Dies ist ein Szenario, in dem der hybride Select-Ansatz verwendet werden könnte.

Schlusswort

Diese ganze Idee begann als ein harmloser CSS-Trick, aber nach all dieser Recherche wurde mir wieder einmal bewusst, dass die Schaffung einzigartiger Erlebnisse ohne Kompromisse bei der Barrierefreiheit keine leichte Aufgabe ist.

Das Erstellen wirklich zugänglicher Select-Komponenten (oder jeder Art von Dropdown) ist schwieriger, als es scheint. WCAG bietet hervorragende Anleitungen und Best Practices, aber ohne spezifische Beispiele und vielfältige praktische Anwendungsfälle sind die Richtlinien meist nur aspirativ. Ganz zu schweigen davon, dass die ARIA-Unterstützung lauwarm ist und native <select>-Elemente zwischen Browsern unterschiedlich aussehen und sich verhalten.

Der "hybride" Select ist nur ein weiterer Versuch, einen gut aussehenden Select zu erstellen und gleichzeitig so viele native Funktionen wie möglich zu erhalten. Sehen Sie diese Technik dieses Experiment nicht als Ausrede, um die Barrierefreiheit zu vernachlässigen, sondern als Versuch, beide Welten zu bedienen. Wenn Sie die Ressourcen, die Zeit und die erforderlichen Fähigkeiten haben, machen Sie es richtig und stellen Sie sicher, dass Sie es mit verschiedenen Benutzern testen, bevor Sie Ihre Komponente in die Welt entlassen.

P.S. Denken Sie daran, einen richtigen Namen zu verwenden, wenn Sie eine "Dropdown"-Komponente erstellen. 😉