18.2 Managing keyboard focus in custom widgets with the HTML tabindex attribute

Was ist Fokus-Management und warum ist es wichtig?

Native HTML-Elemente bringen die Keyboard-Funktionalität (und so mit den automatischen Fokus) schon mit. Für eigene, komplexere Widgets (wie z.B. Tabgroups), die ein ähnlich effizientes Tabben ermöglichen sollen, braucht man JavaScript und das tabindex-Attribut. Dieses ist das grundlegende Attribut zum Fokus-Management.

Tabindex kann:

  • ein Element programmatisch fokussierbar machen
  • einem Element erlauben, fokussierbar zu sein (oder eben nicht), d.h., man kann es aus der Fokusreihenfolge der Seite ausschließen oder darin einbeziehen
  • die Position eines Elements innerhalb der Tabreihenfolge verändern – das sollte man aber vermeiden!!!

tabindex="0"

tabindex="0" macht ein Element fokussierbar und tabbar und schließt es in die laufende Tab-Reihenfolge der Seite ein. Die Tab-Reihenfolge folgt dabei dem Quellcode. Native Elemente benötigen keinen Tabindex.
Wenn man ein nicht-interaktives Element, wie z.B. ein <div> umfunktioniert, um ein eigenes, interaktives Widget zu bauen, setzt man tabindex="0", um das Element in die Tabreihenfolge der Seite aufzunehmen. Wenn tabindex="0" gesetzt ist, muss das Element zugleich die passende ARIA-role bzw. ARIA-name haben, um den AT zu kommunizieren, was es ist und was es tut (=wie es heißt).

tabindex="-1"

tabindex="-1" (oder ein beliebiger anderer negativer Wert, -1 und -999 werden komplett gleich behandelt) machen das Element ebenso programmatisch fokussierbar aber es wird nicht in die natürliche Tabreihenfolge der Seite aufgenommen. Man kann z.B. die JavaScript focus() Methode verwenden, um den Fokus auf das Element zu legen, aber es wird mit der Tab-Taste keinen Fokus erhalten.
Um das Element nicht fokussierbar zu machen, müsste man das HTML-Attribut disabled oder inert verwenden.
tabindex="-1" ist nützlich für Elemente, die man fokussierbar machen möchte, aber die ansonsten nicht in der Tabreihenfolge enthalten sein sollen (z.B. wenn man nicht möchte, dass der User den Fokus dorthin bewegen kann). Beispiel dafür: eine Zusammenfassung der Eingabefehler bei einem Formular, die automatisch nach dem gescheiterten Übermittlungsversuch fokussiert werden. Damit ist sichergestellt, dass Screenreader diese Fehlerzusammenfassung erkennen und der User die erforderlichen Schritte unternehmen kann.

tabindex="+1"

AVOID POSITIVE TABINDEX-VALUES!

Ein Element mit einem positiven Tabindex (+1, +2, +3, ...) wird aus dem DOM herausgenommen und direkt an den Anfang gestellt – das verursacht generell Verwirrung für User und ATs. Der Browser arbeitet zuerst alle positiven Tabindex-Einträge ab, dann beginnt er mit der natürlichen Reihenfolge des Quellcodes.

FF-DevTools - Barrierefreiheit - Checkbox Tabreihenfolge anzeigen

Chrome Extension »Accessibility Insights« zeichnet einen Tab-Reihenfolge-Pfad

Relevante Success Criteria

SC 2.4.3 Focus order (Level A)
If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components recieve focus in an order that preserves meaning and operability.

Roving Tabindex Technique

Roving Tabindex bedeutet, dass der Tabindex rotiert – bei eigenen custom Widgets wird mittels JavaScript zwischen tabindex="0" und tabindex="-1" gewechselt.
Der roving Tabindex ermöglicht es zu kontrollieren, wo (und wie) sich der Fokus innerhalb einer Gruppe von Kindelementen bewegt, die als ein einziger Tabstop behandelt werden soll.

Der roving Tabindex Algorithmus

  1. Setze tabindex="0" auf das Element in der Gruppe, das in der natürlichen Tabreihenfolge integriert werden soll und setze tabindex="-1" für alle anderen fokussierbaren Elemente dieser Gruppe
  2. Wenn der Keyboard-Fokus innerhalb der Gruppe liegt und der User eine Navigationstaste drückt (wie left arrow oder right arrow):
    1. Setze tabindex="-1" auf das Element, das ursprünglich den tabindex="0" gehabt hat.
    2. Setze tabindex="0" auf das Element, das aufgrund des Key-Events fokussiert wird.
    3. Setze den Fokus auf das (neue) Element, das den tabindex="0" hat, mittels JavaScript focus() Methode.

Beispiel: WYSIWYG-Menu-Buttons

Manche Buttons sind Toggle-Buttons, manche sind Checkboxen – je nachdem, welche Auswahl möglich sein soll. Beide können verschiedene Zustände (states) annehmen, z.B. gedrückt oder nicht (pressed/not pressed), bzw. gibt es auch mixed states (wenn die Formatierung nicht für alle Elemente gilt, die gerade markiert sind). Für diese Zustände gibt es kein natives HTML-Attribut.
aria-pressed kommuniziert diesen Zustand: [ false | mixed | true | undefined ].
Wir wissen schon: Das <button>-Element wird automatisch auf die ARIA-Rolle role="button" gemappt und vom Screenreader als Button vorgelesen. Die Attribute aria-pressed und aria-haspopup verändert die Semantik des Elements, manche Browser und AAPI-Kombinationen geben dem Button dann eine Rolle als Toggle-Button

<!-- a button without a state -->
<button>Do Something</button>
<!-- a toggle button with pressed state -->
<button aria-pressed="true">Dark theme</button>

Los geht's ... mit einem generischen <div>-Container, dem die Rolle "Toolbar" zugewiesen wird. Diese Rolle zeigt den Container als "Toolbar" an und kommuniziert, dass die Buttons innerhalb zusammengehören.

<div role="toolbar">
  <!-- Icon-Buttons with accNames (11.3) -->
</div>

Edit Buttons

Press the tab key to navigate through links and button on this page.

A link is an interactive element outside the toolbar.

Links

aria-states lassen sich gut als Hook für CSS-Styles verwenden.
Und nicht auf die focus-styles vergessen! Mit der :focus-within-Pseudoklasse kann die gesamte Toolbar einen subtilen Fokus erhalten, sobald ein Element darin aktiv ist.

Links