Fortsetzung dieses Beitrags.
Die Navigation mit Pfeiltasten hat den großen Vorteil, dass die Liste der mittels Tabulatortaste fokussierbaren Elemente weniger umfangreich ist. Das führt dazu, dass mit der Tastatur schneller zwischen verschiedenen Bereichen der Seite navigiert werden kann. Es kann auch als eine Form von Progressive Disclosure betrachtet werden: Benutzer entscheiden anhand des Labels der Tabliste selbst, ob sie diesen Bereich der Seite weiter erkunden möchten oder nicht, und werden nicht genötigt, erst durch alle Tabs hindurch zu navigieren, bevor sie eventuell interessantere Inhalte erreichen.
Eine weitere sinnvolle Anforderung ist, dass die Navigation mit den Pfeiltasten abgeschlossen sein sollte. Das heißt, bei einer horizontal orientierten Tabliste sollte das Drücken der "Pfeil nach links"-Taste, wenn der Fokus auf dem ersten Tab in der Liste liegt, dazu führen, dass zum letzten Tab gesprungen wird. Das Drücken der "Pfeil nach rechts"-Taste, bei Fokus auf dem letzten Tab, soll den Fokus entsprechend zum ersten Tab der Liste verschieben. Bei einer vertikal orientierten Tabliste erfolgt die Navigation analog. Darüber hinaus kann die Usability verbessert werden, indem man erlaubt, mit der "Ende"-Taste zum letzten Tab zu springen, und mit der "Pos 1"-Taste zum ersten. Das erleichtert die Bedienung, besonders bei Tablisten mit vielen Einträgen.
Fokus
Es wird also innerhalb der Tabliste nicht mit der Tabulatortaste, sondern mit den Pfeiltasten navigiert. Das bedeutet, dass zu jedem Zeitpunkt nur ein Tab durch das Drücken der Tabulatortaste den Fokus bekommen kann. Tabt der Benutzer von außerhalb des Widgets in die Tabliste hinein, darf nur der aktuell ausgewählte Tab den Fokus erhalten. Ausgehend von diesem Tab wird beim Drücken der entsprechenden Pfeiltasten – programmatisch durch den Aufruf der Methode focus
– der Fokus von einem Tab zum nächsten verschoben. Besitzt einer der Tabs den Fokus und der Benutzer drückt die Tabulatortaste, wird entweder das erste interaktive Element innerhalb des ausgewählten Tabpanels, oder das Tabpanel selbst fokussiert.
Es sollte grundsätzlich vermieden werden, Elemente, die nicht interaktiv sind, per Tastatur fokussierbar zu machen, da die Benutzer mit der Fokussierbarkeit die Erwartung verbinden, irgendeine Aktion auslösen zu können. In diesem speziellen Fall kann es aber dennoch angebracht sein, dem Tabpanel Fokus zu geben, damit keine Inhalte übersprungen werden. Besteht diese Gefahr im konkreten Fall nicht, sollte man den Dingen ihren Lauf lassen. Wird dem Benutzer erlaubt, den Fokus auf das Tabpanel zu setzen, oder entscheidet man sich, den Fokus auf das Panel programmatisch zu setzen, gilt wie immer, dass dies auch visuell erkennbar sein muss. Mit Hilfe der Pseudoklasse :focus
kann ein entsprechender Stil auch für ein gegenwärtig fokussiertes Tabpanel definiert werden.
Damit ein Element durch das Drücken der Tabulatortaste den Fokus erhalten kann, muss es entweder wie im Fall von Buttons oder Links interaktiver Inhalt sein, oder aber über ein gesetztes tabindex
-Attribut mit einem nicht-negativen Wert verfügen. Positive Werte für dieses Attribut sollten allerdings prinzipiell vermieden werden: Manuelle Eingriffe in die Tab Order führen in aller Regel nur dazu, dass die Benutzbarkeit der Seite eingeschränkt wird. Die Reihenfolge, in der interaktive Elemente squenziell fokussiert werden, sollte danach bestimmt werden, in welcher Reihenfolge diese Elemente im Markup stehen.
Die Elemente, die auch mit der Tabulatortaste fokussierbar sein sollen, bekommen das Attribut tabindex
mit dem Wert 0
verpasst. Das betrifft den aktuell ausgewählten Tab, sowie unter Umständen die zu den Tabs gehörenden Panels. Elemente, die ausschließlich programmatisch den Fokus erhalten sollen, etwa als Reaktion auf das Drücken der linken oder rechten Pfeiltaste, bekommen hingegen ein tabindex
-Attribut mit dem Wert -1
. Das betrifft diejenigen Tabs, die gegenwärtig nicht ausgewählt sind. Navigiert der Benutzer durch die Tabliste, sind entsprechend nicht nur die aria-selected
-Attribute der Tabs, sondern auch die tabindex
-Attribute an die aktuelle Auswahl anzupassen.
Auswahl
Es bleibt die Frage, wann ein Tab ausgewählt und dessen Inhalt angezeigt werden soll. Bereits dann, wenn der Tab den Fokus erhält oder erst, wenn der Tab durch das Drücken der Leer- oder Entertaste aktiviert wird? Im Allgemeinen ist das wohl Geschmackssache. Welche der beiden Varianten die Nutzer deiner Seite erwarten, hängt von ihren Erfahrungen mit dieser Art Widgets ab, die wesentlich durch die entsprechenden Implementierungen der von ihren Betriebssystemen genutzten GUIs geprägt werden. Diese Implementierungen sind aber nicht einheitlich, weshalb man es diesbezüglich nicht allen Nutzern gleichermaßen recht machen kann.
Es gibt allerdings Bestrebungen, für Webseiten und Webanwendungen einen einheitlichen Standard zu etablieren. Danach sollte ein Tabpanel bereits eingeblendet werden, wenn der Nutzer zum zugehörigen Tab navigiert. Das heißt, die Auswahl folgt dem Fokus. Das gilt allerdings nur unter der Vorraussetzung, dass der anzuzeigende Inhalt bereits geladen und geparst wurde. Ist das nicht der Fall, sollte definitiv auf eine Aktivierung durch den Benutzer gewartet werden. Das könnte sonst zu einem Usability-Desaster führen, wenn die Navigation der Tabliste durch das Laden der Inhalte merklich verzögert wird. Wenn die Inhalte der Tabpanels dynamisch geladen werden, sollte man sich gegebenenfalls eine Preloading-Strategie überlegen, um Wartezeiten bei der Navigation zu vermeiden.
Erreichbarkeit
Was bei diesem Thema oft übersehen wird, ist die Frage der Erreichbarkeit von Inhalten, die in einem Tabbed Interface zusammengefasst sind. Enthält ein Tab Linkziele, entweder das Tabpanel selbst oder ein Element darin, und ist dieser Tab nicht der Tab, der gerade ausgewählt ist, dann sind diese Ziele über die entsprechende URL nicht erreichbar. Aus Benutzersicht ist das alles andere als optimal. Angenommen eine Nutzerin besucht die Seite zuerst mit ihrem Smartphone, wo alle Inhalte angezeigt werden, und setzt ein Lesezeichen auf ein Linkziel innerhalb der Seite. Später ruft sie das Lesezeichen auf dem Laptop ab, wo die Inhalte in Tabs verpackt sind, und findet nicht wonach sie sucht. Das ist keine gute Nutzungserfahrung.
Es sollte also beim Programmstart geprüft werden, ob die URL einen Fragmentbezeichner enthält, der entweder einen nicht ausgewählten Tab oder ein Element darin adressiert. Ist das der Fall, sollte dieser Tab der anfänglich ausgewählte Tab werden. Die Prüfung der URL kann mit der Eigenschaft window.location.hash
erfolgen. Es sollte aber auch auf Eingaben des Benutzers reagiert werden, die zu einem späteren Zeitpunkt erfolgen. Zu diesem Zweck kann auf window
ein Eventhandler für das popstate
-Ereignis registriert werden. Dieses Ereignis wird bei Eingaben in die Adressleiste des Browsers ausgelöst, ebenso wie bei der Navigation der History des Browsers mit dessen "Vor"- und "Zurück"-Buttons.
Schließlich wird es in den meisten Fällen auch sinnvoll sein, die URL an den gerade ausgewählten Tab anzupassen. Dazu können die Methoden pushState
und replaceState
des Objektes window.history
verwendet werden. Wann immer der ausgewählte Tab wechselt, wird ein neuer Eintrag auf den History Stack gelegt. Wird zudem wie empfohlen auf den Eintritt des Ereignisses popstate
reagiert, können die Benutzer über die History des Browsers durch die schon besuchten Tabs navigieren. Außerdem macht es eine solche Implementierung sehr viel einfacher, Lesezeichen auf die einzelnen Tabs zu setzen, so, wie es bei einer als Fallback verwendeten Navigation auch möglich ist.
Du siehst also, eine richtige™ Umsetzung von Tabs bedeutet eine Menge Aufwand. Sehr wahrscheinlich habe ich sogar noch das ein oder andere Detail vergessen und bei einigen Sachen bin ich nicht hundertprozentig sicher, was aktuell die Best Practices sind. Die gute Nachricht für dich ist, dass du dir den Aufwand vermutlich sparen kannst.
Wenn ich mir deinen Code so ansehe, drängt sich mir der Gedanke auf, dass du eigentlich gar keine Tabs willst. Das sieht mir eher danach aus, dass du beim Klick auf "Benutzer" oder "Passwort ändern" im Sinne einer SPA eine komplett neue Ansicht laden willst. Falls dem so wäre, solltest du nicht versuchen den Eindruck zu erwecken, es würde sich um Tabs handeln, weder dir selbst, noch den Benutzern deiner Seite gegenüber. Verwende stattdessen Links auf externe Ressourcen als Fallback und wenn JavaScript verfügbar ist, registrierst du für diese Links Eventhandler, in denen du mit preventDefault
den Sprung zu einem anderen Dokument unterbindest.
Im Anschluss kannst du die Fetch API dazu verwenden die gewünschten Inhalte zu laden. Wenn sie da sind, baust du sie in die Seite ein, aktualisierst den Inhalt des title
-Elements und setzt den Fokus entweder auf die geladene Überschrift oder den Container. Zum Thema Fokussierbarkeit siehe die Absätze oben. Für den Fall, dass du den Fokus auf das Element setzt, dass die neuen Inhalte gruppiert, solltest du dieses Element mit dem Attribut aria-label
oder dem Attribut aria-labelledby
entsprechend beschriften.
Viele Grüße,
Orlok
„Dance like it hurts.
Make love like you need money.
Work when people are watching.“ — Dogbert