Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Tri-State-Checkbox: Mit Tastatur und Screenreader bedienbar

Einstieg in ARIA 1.0 – Teil II

Tri-State-Checkbox: Mit Tastatur und Screenreader bedienbar

Mit ARIA 1.0 können fortgeschrittene Steuerelemente für Screenreader barrierefrei gemacht werden. Insbesondere müssen die Tastatursteuerung und Semantik berücksichtigt werden. Wie eine konkrete Umsetzung aussehen kann, zeigt das Beispiel einer Tri-State-Checkbox.

WAI-ARIA ist die Brücke zwischen dynamischen Anwendungen und Barrierefreiheit in Screenreadern. Aus der WAI-ARIA-Spezifikation ist bislang nur wenig als offizielle Empfehlung des W3C verabschiedet worden — allen voran das gestern vorgestellte role-Attribut – aber vieles funktioniert bereits in gängigen Screenreadern wie JAWS oder dem kostenlosen NVDA.

WAI-ARIA dient unter anderem dazu, zwei Aspekte des Webdesigns zu verbessern: die Tastaturbedienbarkeit und die Semantik. Diese sind vor allem in dynamischen Anwendungen oft ein Problem. Es handelt sich bei WAI-ARIA um einen Satz zusätzlicher Attribute für HTML (und andere Techniken), die – korrekt eingesetzt – eine Menge bringen können, insbesondere für Screenreader-Nutzer. Die Praxis zeigt aber auch, dass WAI-ARIA aktuell oft nach dem Gießkannenprinzip eingesetzt wird; mit der Folge, dass manche Webseiten weniger zugänglich sind als zuvor.

Ein einfaches Beispiel

Wie WAI-ARIA sinnvoll eingesetzt wird, zeigt das folgende Beispiel anhand einer einfachen widget role, einer Tri-State Checkbox. Die Tri-State-Checkbox ist ein Kontrollkästchen, das drei Zustände (nicht aktiviert, teilweise aktiviert und aktiviert) annehmen kann.

Tri-State-Checkboxen in den Zuständen 'nicht aktiviert', 'teilweise aktiviert' und 'aktiviert'
Mehr als nur HTML erforderlich

Obwohl es in HTML die Checkbox gibt, kann sie in HTML nur zwei Zustände annehmen (aktiviert oder nicht aktiviert). Der dritte Zustand »teilweise aktiviert« ist in HTML nicht vorgesehen.

Üblicherweise wird bei Tri-State-Checkboxen mit Grafiken gearbeitet, damit auch der Zustand »teilweise aktiviert« dargestellt werden kann. Ein Blick in den Code im wilden Web weist meist wenig Erhellendes auf, denn die komplette Funktionalität wird mit JavaScript bereitgestellt. So ist das Grundgerüst meist nicht viel mehr als ein SPAN:

  1. <span id="meineID" onmousedown="foo();"></span>

Zweck der Übung

Wenn ein span-Element die Grundlage für ein Widget ist, wird jedem sicher schnell klar, dass das kaum barrierefrei sein kann. Solche Inhalte werden mit der Tastatur nicht bedienbar sein und sie weisen keinerlei Semantik auf; vor allem wenn die span-Elemente der Positionierung von Hintergrundgrafiken dienen, werden diese Inhalte im Kontrastmodus des Browsers unsichtbar sein. Dabei gibt es für die meisten Widgets barrierefreie Lösungen, die nur einer Prise WAI-ARIA bedürfen. Folgende Aspekte müssen in Angriff genommen werden:

  • Das Element muss mit der Tastatur erreicht und bedient werden können.
  • Das Element muss als Checkbox identifiziert werden können (Rolle).
  • Der Zustand (aktiviert, teilweise aktiviert, nicht aktiviert) muss identifizierbar sein.
  • Das Steuerelement benötigt einen Namen (Beschriftung/label).

Der Kontrastmodus wurde hier nicht vergessen. Er wird durch die anderen Maßnahmen abgedeckt – zumindest in diesem konkreten Beispiel.

Tastaturbedienbarkeit

Wir haben es zunächst mit einem span zu tun, das praktisch keine Eigenschaften hat. Damit das Element mit der Tastatur angesteuert werden kann, benötigt es ein tabindex-Attribut:

  1. <span tabindex="0" onkeydown="bar();" id="meineID" onmousedown="foo();"></span>

Die Funktion, die mit dem onkeydown-Attribut aufgerufen wird, dient dazu, die zulässigen Tastendrucke für die Bedienung der Checkbox (Leertaste, Eingabetaste) zu ermitteln, um dann erst die eigentliche Funktion aufzurufen. Das kann im einfachen Fall wie folgt aussehen:

  1. function bar()  {
  2.   if (event && event . type == 'keydown') {
  3.     if (event . keyCode) {
  4.       tastenCode = event.keyCode; /* IE */
  5.     }
  6.     else if (event . which) {
  7.       tastenCode = event . which; /* Mozilla, Safari, Opera */
  8.     }
  9.     if (tastenCode != 13 && tastenCode != 32) {
  10.       return true;
  11.     }
  12.   }
  13.   foo();
  14. }

Die Funktion soll nur bei Druck der Eingabe- oder Leertaste ausgelöst werden, nicht aber bei anderen Tasten. Das Filtern der Tastendrucke kann natürlich auch in der mausabhängigen Funktion eingebaut werden; in dem Fall sollte anstatt der beiden geräteabhängigen Event-Handler der Event-Handler onclick verwendet werden (bei Links und Formularelementen reagiert er auch auf Eingabe- und Leertaste).

Die Tastaturbedienbarkeit lässt sich einfacher mit einem interaktiven Element umsetzen. Da Grafiken für die drei Checkbox-Zustände benötigt werden, bietet sich ein input-Element des Typs image oder ein button-Element an. Wer größere Gestaltungsspielräume braucht, wird das button-Element einsetzen; für den einfachen Austausch von Grafiken reicht das input-Element:

  1. <input type="image" src="nicht-aktiviert.png"  alt="Alle Optionen auswählen" id="meineTriStateID" />

Der Browser erkennt dieses Element und stellt die Tastaturbedienbarkeit bereit. Wenn also auf native interaktive HTML-Elemente zurückgegriffen werden kann, müssen keine weiteren Maßnahmen ergriffen werden, um das Element per Tabulatortaste erreichbar zu machen. Ein weiterer Vorteil des input-Elements ist, dass die Grafik auch im Kontrastmodus dargestellt wird.

Semantik hinzufügen

Ob mit span, input oder button: dem Element müssen noch Rolle und Zustand hinzugefügt werden. Die Rolle (role) des Elements ist eine Checkbox und der ursprüngliche Zustand (aria-checked) ist »nicht aktiviert«:

  1. <input role="checkbox" aria-checked="false" type="image" src="nicht-aktiviert.png" alt="Alle Optionen auswählen" id="meineTriStateID" />

Das aria-checked-Attribut kann die Werte false, mixed oder true annehmen. Ohne dieses Attribut ist das role-Attribut wertlos. Das heißt: Nur wenn beide Attribute gemeinsam eingesetzt werden, wird diese Checkbox auch für Screenreader-Nutzer semantisch richtig dargestellt werden können.

Im nicht-ARIA-fähigen Browsern ist der Name des Elements der Wert des alt-Attributs. In ARIA-fähigen Browsern muss aber das spezifischere Attribut aria-label eingesetzt werden, damit die Screenreader die Grafik richtig identifizieren können. Diese beiden Attribute sollten den gleichen Wert erhalten.

  1. <input aria-label="Alle Optionen auswählen" role="checkbox" aria-checked="false" type="image" src="nicht-aktiviert.png" alt="Alle Optionen auswählen" id="meineTriStateID" />

Wenn der Zustand der Checkbox sich nun ändert, muss das aria-checked-Attribut zusammen mit der Grafik, dem Alternativtext und der Beschriftung aktualisiert werden.

Demonstration

Die Funktionalität kann nachfolgend getestet werden. Ohne Screenreader kann lediglich die Tastaturbedienbarkeit überprüft werden. Die Semantik der Checkbox kann nur mit einem Screenreader getestet bzw. mit der Funktion unter den Checkboxen kontrolliert werden.

Beispiel: Tri-State-Checkbox mit WAI-ARIA

Welche Techniken beherrschst Du?

Die Demonstration gibt es auch als Download (ZIP).

Zusammenfassung

Für alle fortgeschrittenen Steuerelemente, die mit HTML alleine nicht abgebildet werden können, gilt, dass sie mit WAI-ARIA erst barrierefrei werden können. Folgende Schritte müssen dabei eingehalten werden:

  1. Tastaturbedienung gewährleisten
    • Jedes Steuerelement muss mit der Tastatur fokussiert werden können.
    • Die Tastatursteuerung von Widgets muss zusätzlich berücksichtigt werden; sie folgt anderen Mustern als die Maus- oder Touch-Screen-Eingabe.
  2. Semantik hinzufügen
    • Jedes Element benötigt eine Rolle. Wenn kein passendes HTML-Element eingesetzt werden kann, dann wird die Rolle mit dem role-Attribut zugewiesen.
    • Für Widgets gibt es zahlreiche Zustände und Eigenschaften. Diese Semantik muss mit ARIA-Attributen zugewiesen werden.
    • Im JavaScript müssen die ARIA-Attribute aktualisiert werden, wenn Zustände oder Eigenschaften der Widgets geändert werden.
  3. Ergebnisse mit Screenreadern testen.

Kommentare

Gunnar Bittersmann
am 08.12.2013 - 09:54

Immer gern gelesen: ein Artikel, der über allgemeines Bla hinausgeht und am konkreten Beispiel zeigt, was zu tun ist.

Ein kleiner Fußnägel-Aufroller ist aber auch dabei:

  1. if (event . keyCode) {
  2.   tastenCode = event.keyCode; /* IE */
  3. }
  4. else if (event . which) {
  5.   tastenCode = event . which; /* Mozilla, Safari, Opera */
  6. }

Das kann man in JavaScript besser als einfachen Einzeiler schreiben – mit dem ||-Operator, der etwas mehr ist als ein logisches Oder:

  1. tastenCode = event.keyCode || event.which;

So wird der Code übersichtlicher.

Permanenter Link

nikosch
am 10.12.2013 - 13:57

Viel gruseliger ist die Verwendung einer globalen Variable an dieser Stelle.

Permanenter Link

Matthias
am 09.12.2013 - 08:32

Danke für diesen interessanten Artikel, auch im Zusammenhang mit dem gestrigen wird noch mal die Bedeutung der role- und aria-Attribute herausgestellt. Es ist wichtig, dass es hochwertige Artikel auch in deutscher Sprache gibt.

Ein kleiner Krümel findet sich immer: "einige" schreibt man klein, auch wenn man sie beherrscht. Das merkt man vor allem dann, wenn man den Satz vervollständigt: "Ich beherrsche einige Techniken."

Permanenter Link
Nicolai Schwarz

Nicolai Schwarz (Webkraut)
am 09.12.2013 - 10:14

Wohoo! Ein Rechtschreib-Kommentar! Gibt es heutzutage ja kaum noch. Stimmt. Ich hab’s geändert.

Permanenter Link

nikosch
am 10.12.2013 - 14:12

Thematisch erinnert mich der Artikel leicht an den Youtube-Beitrag von Google (Link erlaubt? http://www.youtube.com/watch?v=x18vEEfpK3g). Leider finde ich das Beispiel einer 3-State-Checkbox nicht sonderlich geeignet, wenn es um das Thema Screenreader geht. Ich bin nicht sensorisch eingeschränkt, aber ich denke, dass wir Sehenden eine Gruppe von Checkboxes scannen und dann über den Zentralknopf steuern mögen, dass dieses Prinzip (bzw. diese Option) für Screenreader-Nutzer aber weit weniger Sinn machen dürfte, weil sie dann ja letztlich jeden Punkt der Gruppe memorieren müssten, um die Gruppe dann global zu selektieren. Und, da sie erst alle Punkte lesen müssen, müsste die Select-All-Box letztlich auch ans Ende der Gruppe. So gesehen halte ich den Artikel für leicht akademisch.

PS: Und das Aria-Labeling sollte sich IMHO je nach Zustand ändern. Also „alle abwählen“ wenn bspw. alle ausgewählt sind, bzw. „alle abwählen“ ODER „Auswahl aufheben“, wenn einige ausgewählt sind. Wie sich diese Auswahlbuttons bei Teilselektierungen verhalten, ist für Sehende meist schon kaum einzuschätzen.

Permanenter Link
Jan Hellbusch

Jan Hellbusch (Autor)
am 10.12.2013 - 17:37

Das YouTube-Video kannte ich nicht, sondern habe mich an der ARIA-Spec orientiert. Die Sache mit dem akademisch kann man vielleicht so stehen lassen - oder gleich zurück geben ;-) Allerdings würde ich weder die Beschriftung (aria-label) dynamisch anpassen noch das Kontrollelement am Ende der Checkboxen stellen: * Der Zustand der Tri-State-Checkbox wird durch aria-checked bestimmt, und der Screenreader gibt bei "aktiviert", "nicht aktiviert" oder "teilweise aktiviert" entsprechende Infos an den Nutzer weiter. Wenn aria-label angepasst würde würde im Screenreader ein Wirr-Warr entstehen "Alle optionen wählen nicht aktiviert", "Alle Optionen abwählen aktiviert" usw. Halte ich also für eine sehr schlechte Idee. * Die Positionierung am Ende ist aus den genannten Gründen nachvollziehbar, aber nur wissenschaftlich. Die Tri-State-Checkboxen sind in der Praxis oft auch in ausklappbaren Baumstrukturen integriert, wo üblicherweise die Unterpunkte unterhalb des Oberpunkts aufgeführt werden. Als Screenreadernutzer würde ich die Unterpunkte auch dort erwarten, nicht oberhalb.
Permanenter Link

nikosch
am 10.12.2013 - 18:15

> "Alle optionen wählen nicht aktiviert"
Das ist zugegebenermaßen verwirrend. Umso mehr würde ich empfehlen, für die Aktion überhaupt keine Checkbox, sondern einen Button zu verwenden. Die Checkbox an dieser Stelle ist historisch gewachsen und war vor ein paar Jahren oft von einem Select mit Action-Optionen begleitet (Typische Anwendung: Webmailer). In Zeiten von JS-basierten UI-Erweiterungen ist die Checkbox hier eigentlich hinfällig: Serverseitig kann man sie nicht auswerten, weil sie potentiell einen anderen Zustand als die repräsentierten Gruppenelemente besitzen kann. Clientseitig ist sie kein echtes Feedbackelement. a) ist der Zustand an den gruppierten Elementen ablesbar b) könnte er durch eine einfach Anzeige wie „6/20 Elementen ausgewählt“ wesentlich besser abgebildet werden, weil c) der Zwischenzustand weder Element-nativ*) ist, noch genug Aussagekraft besitzt.
Was die Baumstrukturen angeht: letztlich wäre das IMHO Aufgabe des FE-Designers, das Markup Screenreader-freundlich zu gestalten und die Anzeige erwartungskonform. So etwas ist machbar. Denkbar wäre auch, nur für den Baum eine entsprechende Checkbox per JS zu generieren und die für Screenreader zu verbergen. Ich bin kein Experte in Barrierefreiheit, könnte mir aber vorstellen, dass eine semantische Gruppierung zusammengehöriger Elemente hier wesentlich leistungsfähiger sein kann, als eine Baumstruktur mit einem globalen Schalter. Meinetwegen auch ein verborgenes Optionsmenü, das die Optionen „Gruppe: Alle wählen“ und „Gruppe: alle abwählen“ direkt enthält.

*) Eine Checkbox ist ein An-Aus-Schalter. Hier abweichend von dieser Norm einen dritten Zustand einführen zu wollen, würde ich dringend überdenken, wenn ich eine Anwendung plane.

Permanenter Link
Jan Hellbusch

Jan Hellbusch (Autor)
am 11.12.2013 - 12:24

Natürlich kann in der Praxis über verschiedene Lösungen für konkrete UI Komponente diskutiert werden. In jedem (vereinfachten) Beispiel findet man zudem auch ein Haar in der Suppe. Dieser Beitrag dient als Beispiel für den am Vortag veröffentlichten Artikel zum role-Attribut. Wesentliche Aussage ist dabei: Das role-Attribut ist ohne die dazugehörigen Attribute für die Zustände der UI-Komponente u.v.v. wertlos. Anders gesagt: Wer ARIA einsetzt, muss es auch zu Ende denken. Die gewählte UI-Komponente einer Tri-State-Checkbox stellt dabei ein einfaches und leicht nachvollziehbares Beispiel dar.

Zu den Äußerungen:

* Wenn die hinterlegte Funktion der Tri-State-Checkbox im Beispiel angeschaut wird, dann gibt es tatsächlich die zwei Funktionen "Alle abwählen" und "Alles auswählen". Diese kann man mit Schaltflächen ersetzen und - je nach Zustand der Checkboxen - dynamisch anpassen. Genauso kann man für den "Zwischenzustand" ("teilweise aktiviert") eine präzisere Statusmeldung ausgeben ("6 von 20" aktiviert" o.ä.). Die Statusmeldung muss dabei im Formularmodus des Screenreaders gelesen werden können, d.h. die Meldung muss entweder in einem Formularelement stehen oder mit einem Formularelement explizit verknüpft werden; nur dann würden Screenreader auch im Formularmodus darauf zugreifen können.
* Der Abruf des Zustands (server- oder clientseitig) ist hier m.E. fehl am Platz und ohne Belangen. ARIA dient dazu, in Markupsprachen u.a. Semantik hinzuzufügen. Den Beitrag muss man so lesen, dass _wenn_ eine Tri-State-Checkbox eingesetzt wird, dann die genannten ARIA-Attribute zum Einsatz kommen müssen. Die geäußerte Kritik zeigt mir, dass der Zweck von ARIA offenbar nicht verstanden wurde. Es geht hier vor allem darum, visuell erkennbare Elemente/Zugehörigkeiten/etc. semantisch abzubilden. Es ist kein Pro/Kontra für Tri-State-Checkboxen (s.o.).
* Sicher gibt es zahlreiche Ansätze, Inhalte zu gruppieren, um den Zugriff durch Screenreader zu optimieren, worauf hier verzichtet wurde, denn das hat ebenfalls nichts mit dem konkreten Thema des role-Attributs zu tun.
* Es geht im Übrigen nicht darum, eine neue UI-Komponente einzuführen. Das ist bereits schon längst spezifiziert: http://www.w3.org/TR/wai-aria/complete#checkbox

Permanenter Link

Die Kommentare sind geschlossen.