2009-02-07 jQuery Accessible Tabs - Wie man Tabs WIRKLICH zugänglich macht
Während viele Tabs-Scripts behaupten zugänglich zu sein sind es die meisten leider weit davon entfernt. Während ich dieses jQuery Plugin zusammen mit meinem Kollegen Artur Ortega entwickelte, suchten wir nach existierenden Javascript Tabs die Artur mit seinem Screenreader tatsächlich bedienen kann. Wir gaben die Suche irgendwann auf.
Das Problem, dass selbst die besseren Scripts haben, ist die Fehlende Rückmeldung an den Nutzer, dass tatsächlich etwas Passiert. Die meisten Tabs Scripts ändern das Aussehen der Tabs und die Sichtbarkeit der zugehörigen Inhalte aber lassen den Nutzer da zurück wo er war - auf dem Tab auf den geklickt wurde - ohne Ahnung was gerade passiert ist (vergleichbar mit einem Klick auf die immer noch viel zu beliebten "Blinden Links" (a href="#")).
Das ist auch genau der große Unterschied den dieses Script macht. Während das Verhalten für Nutzer ohne Sehbehinderung exakt die gleiche ist passiert sehr viel unter der Haube. Aber erstmal zurück zum Anfang.
jQuery Accessible Tabs benutzt ein sehr einfaches und flexibles HTML Markup als Basis. Alles was es braucht ist ein Wrapper mit Headlines und Inhalts-Elementen nacheinander. Die einfachste und bestmöglichste Alternative ohne Javascript ohne Auszusehen als würde etwas fehlen oder etwas währe kaputt.
<div class="tabs">
<h2>eine Beispielüberschrift</h2>
<div class="tabbody">
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>
<h3>Lorem ipsum</h3>
<p>Nullam malesuada suscipit pede. Nullam ipsum lacus, varius</p>
</div>
<h2>noch eine Beispielüberschrift</h2>
<div class="tabbody">
<p>Integer tincidunt. Cras dapibus. Vivamus elementum nisi.</p>
<p>Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel.</p>
</div>
<h2>alles andere</h2>
<div class="tabbody">
<p>Hier könnte Ihr Inhalt stehen</p>
</div>
</div>
Ein einfacher jQuery Aufruf transformiert das Markup in accessible Tabs:
$(document).ready(function(){
$('.tabs').accessibleTabs();
});
Um nicht nur so zugänglich sondern auch so flexibel wie möglich zu sein, kann das Script mit verschiedensten Einstellmöglichkeiten konfiguriert werden.
$('.tabs').accessibleTabs({
// Der Name der Klasse die dem Div zugewiesen
// welche um das Markup herumgeschrieben wird
wrapperClass: 'content',
// Der Name der Klasse die das aktuelle Tab markiert
currentClass: 'current',
// Tag oder valider Query Selector der Elemente
// aus denen die Tabs Navigation erzeugt wird
// (die Originale werden entfernt)
tabhead: 'h4',
// Tag oder valider Query Selector der Elemente die
// als Inhalte der Tabs genutzt werden sollen
tabbody: '.tabbody',
// Anzeigeeffekte: 'fadeIn', 'slideDown' oder 'show'
fx:'show',
// Geschwindigkeit (String|Number): 'slow', 'normal', oder 'fast')
// oder die Milisekunden die die Anzeigeeffekte dauern sollen
fxspeed: 'normal',
// Text um Screenreadern anzuzeigen welches der ausgewählte Tab ist
currentInfoText: 'current tab: ',
// Definition wo der Text eingefügt wird
// Entwender 'prepend' oder 'append'
currentInfoPosition: 'prepend',
// Klasse des span mit dem Infotext
currentInfoClass: 'current-info'
});
Also gut, warum ist dieses Script zugänglicher als andere?
Das, was dieses Script hauptsächlich besser macht als die anderen ist ein Feedback für Nutzer von Screenreadern nach dem Klick. Wenn der Nutzer auf einen der Tabs klickt wird tatsächlich auf der Seite navigiert.
Jeder Link in den Tabs führt tatsächlich zu einem fokusierbaren Inhalt. Um dies zu ermöglichen, erzeugen wir einen benannten Anker in dem zugehörigen Inhalt des Tabs. Dieser Anker, da er keine href-Egenschaft hat, kann von Haus aus aber keinen Fokus erhalten. Um dies zu ermöglichen wird ihm die Eigenschaft tabindex="0" zugewiesen. Warum 0? Weil ein Tabindex von 0 nicht die natürliche Tabordnung des Dokumentes verändert aber dem zugewiesenen Element trotzdem ermöglicht Tab-Fokus zu bekommen.
<h4>
<a id="accessibletabscontent" name="accessibletabscontent" class="accessibletabsanchor" tabindex="0">
Eine Beispielüberschrift
</a>
</h4>
In dem benannten Anker wird der selbe Text wie im Tab eingefügt welches gerade geklickt wurde und das ganze mit einer Headline umklammert. Dies stellt eine sehr angenehme Erfahrung für die Nutzer von Screenreadern dar, da es auf nette Art und Weise bestätigt, dass man gerade zu einem anderen Inhalt auf der Seite gesprungen ist.
Ein weiteres Zusatzfeature für Screenreader Nutzer ist ein kleiner Text der auf den aktuell ausgewählten Tab kennzeichnet. Standardmäßig lautet dieser Text "current tab: " gefolgt von dem Standardtext des Tabs. Dieser Text, ebenso wie seine Position (entweder davor oder danach) ist aber sehr leicht internationalisierbar.
Problem: Das Fokussieren von versteckten oder eben noch versteckt gewesenen Elementen
Während dem Testen der Tabs stießen wir auf ein sehr interessantes und ebenso störendes Verhalten des Screenreaders Jaws (und sehr wahrscheinlich auch von anderen). Wir wollten auf die Headline in dem Inhaltselement eines Tabs fokussieren nachdem es eingeblendet wurde. Bei jedem Test war der Screenreader nicht in der Lage das entsprechende Element zu finden und sprach statt dessen um Ende des Dokumentes oder einer anderen zufälligen Position. Dieses Phänomen wird durch den virtuellen Buffer des Screenreaders verursacht, einer internen Kopie des DOMs in dem der Screenreader navigiert. Der virtuelle Buffer gleicht seinen Inhalt regelmäßig mit dem DOM des Browsers ab aber dies ist stets erst nach einer Verzögerung.
Um um dieses Problem herum zu arbeiten nutzen wir einen anderen Lösungsansatz. Der benannte Anker auf den wir verlinken ist tatsächlich nicht in dem versteckten Inhaltselement sondern im DOM davor und "offscreen" (position:absolute;left:-999em) versteckt. Tatsächlich zeigt jeder Tab auf den exakt selben Anker. Wir schreiben den Inhalt des Headline-Elementes einfach jedes mal neu und blenden das zugehörige Inhaltselement entsprechend ein oder aus. Das empfundene Verhalten ist genau das gleiche. Der Screenreader Nutzer klickt auf den Tab, folgt dem Link und findet den verlinkten Inhalt mit der entsprechenden Headline.
Man kann sich das Script hier in in vielen verschiedenen Beispielen in Aktion ansehen. Das jQuery Plugin ist ebenfalls seit der Version 3.1.1 Teil des CSS Framework Yaml.
Man kann das script zusammen mit den demos hier herunterladen. Der gesamte Code ist ebenfalls auf github.
Vor ca. 1,5 Jahren, im Sommer 2007 sprach ich Dirk Ginader per Skype auf ein kleines Problem an. Damals machte ich gerade meine ersten Schritte mit jQuery und wollte für das Redesign von YAML.de ein Tabscript verwenden, um mehrfache hintereinander aufgelstete Quelltextabschnitte aufgeräumter präsentieren zu können. Zu diesem Zweck sah ich mir verschiedene Tab-Lösungen (mit und ohne jQuery) an, die jedoch alle am gleichen Problem krankten. Die Tabs funktionieren generell nur in Verbindung...
Tracked: Mar 02, 21:11
Dirk Ginder hat vor einiger Zeit mit einen Kollegen zusammen ein “wirklich” zugängliches “jQuery UI Tab Plugin” geschrieben. Da ich mich in den letzten Wochen stark mit dem xHTML & CSS Framework “YAML” und auch ...
Tracked: Oct 07, 15:34
Comments
Gibt es einen Grund warum du einen tabindex von 0 und nicht einen von -1 nimmst? In der Regel will man doch nur selber fokusieren können, aber dem User diese Möglichkeit vorenthalten.
Ich glaube, ich bin auch auf das selbe - von dir oben angesprochene - Problem gestoßen (Leider konnte ich es gerade nicht mehr genau nachstellen.)
Bei mir wurde beim Fokusieren der fokusierte Bereich immer übersprungen und der Inhalt danach vorgelesen.
Meine Lösung war aber eine andere. Ein Delay zwischen sichtbar machen und fokusieren von 180 ms reichte aus. Wenn ich dagegen "Aria-Tab-Semantik" hinzugeügt habe, benötigte ich gar keinen timeout mehr. (Ich habe mich allerdings bei Tabs gegen Aria-Semantik und für einen timeout entschieden).
Grüsse
Alex
P.S.: Du hast noch einen Teil deines englischen Beitrags hier in den deutschen eingefügt.
Mein Kollege Benjamin-Hawkes Lewis, ein wandelndes Lexikon in Sachen Web Development, Browsern und Screenreadern, hat mich auf ein Problem mit tabindex -1 hingewiesen. Opera hat wohl Probleme Elemente mit diesem Tabindex zu fokussieren. Grundsätzlich hast Du aber recht. Ein Tabindex von -1 wäre genau der richtige Ansatz. Aus Kompatibilitätsgründen habe ich aber bewusst darauf verzichtet.
WAI-ARIA ist eine tolle Sache. Leider ist die Unterstützung bei tatsächlich benutzten Browsern und Screenreadern aber noch recht gering. Es spricht zwar absolut nichts dagegen, Webseiten oder Applikationen die bereits zugänglich aufgebaut sind, mit WAI-ARIA noch weiter anzureichern. Sich vollständig darauf zu verlassen ist aber genauso kurzsichtig wie von einer hunderprozentigen Verbreitung von Javascript auszugehen.
Mit dem Delay währe ich sehr vorsichtig. Dieses Timing ist doch sehr systemabhängig und kann je nach Prozessor, Arbeitsspeicher etc. stark variieren.
(Und danke für den Hinweis mit dem englischen Textschnipsel - unfassbar wie ich den immer wieder übersehen habe...
Leider ist das Surfen mit dem IE6 immer noch nicht verboten
Grüße
Gerhard
Wobei ich mich Frage, ob es immer richtig ist, das man eine Überschrift hat, bei einigen Sachen so wie das unter
http://www.holzwurm-page.de/holzarten/abisza.htm macht es keinen Sinn, das alles mit Überschriften zu versehen oder habe ich da jetzt einen Denkfehler ???
h1 Holzwurm-Page.de
--h2 Service
--h2 Benutzeranmeldung
--h2 Holzatlas
----h3 A-Z
----h3 Abachi
------h4 Namen
------h4 Verbreitung
------h4 Bearbeitung
----h3 Abarco
------h4 Namen
------h4 Verbreitung
------h4 Bearbeitung
----h3 und so weiter...
--h2 Holz Sprüche
--h2 Ihre Unterstützung
Hope it helps
Soll heißen: Ich setze einen Link auf tabs.html#ueberschrift1 und die Seite wird mit aktivem Tab 1 geöffnet, bei einem Verweis auf tabs.html#ueberschrift4 wird die Seite mit aktivem Tab 4 geöffnet.
Wäre soetwas möglich?
P.S.: Schon einen Plan für die Veröffentlichung von YAML 3.1.1?
Danke
ich kenne die Richtlinien der WAI nicht, aber was mir an eurem Beispiel negativ auffällt, ist die fehlende Unterstützung des Zurückbuttons sowie die Möglichkeit, ein Bookmark auf einen Tab zu setzen. Gibt es einen Grund, weshalb das bei eurer Plugin-Entwicklung nicht beachtet wurde?
http://www.asual.com/jquery/address/samples/tabs/#/extras.html
Beispiel Tabs mit Anchors.
Wir suche immer noch nach einer Möglichkeit den Backbutton nutzen zu können ohne auf dierse Probleme zu stossen. Für Vorschläge bin ich sehr offen.
Ich bin da zwiegespalten. IMHO ist ein Tabreiter eben keine eigene Seite. Weshalb ich es durchaus logischer finde, bspw. von Eurem Beispiellink auch nach ein paar Tab-Drücken via Back-Button wieder auf diese Seite zu kommen. Für Screenreader-Nutzer mag die Erfahrung anders sein (auch wegen der erzeugten Überschriftensystematik), aber das wäre dann ein genereller Punkt, über den man nachdenken sollte. Im Zweifel wäre dann ein regulärer Request einem dynamischen Tabreiterinhalt vorzuziehen.
code;
.js .jquery_tabs .current-info,
.js .jquery_tabs .accessibletabsanchor{
position:absolute;
left:-999em;
}
Dazu wird beim Start die Klasse »js« dem html-Element hinzugefügt:
document.documentElement.className += " js";
Dort gibt es allerdings einen Work-Around, den man bei eurer Version nicht anwenden kann. Gibt es eine Möglichkeit, auch bei den zugänglichen Tabs problemlos dynamischen JS-Inhalt einzubinden? Ein Bespiel gibt es in der angegebenen URL...
Ich mußte wegen einer Googlemap im Tab dein Script umbauen und habe statt hide() und show() addClass('print') und removeClass('print') verwendet. Der Vorteil der Klasse .print aus dem YAML Core, die Tabs sind nun druckbar, dafür geht Fx nicht mehr. Vielleicht als Idee in den Options konfigrierbar machen: Entweder druckbare Tabs oder EffeKte.
Hier der Hintergrundartikel in meinem Blog.
Ich bin im Moment im Begriff dein Script in Mootools umzusetzten, da ich leider dieses Framework einsetzten muss und es für eine gute Fingerübung im Bereich Accessibility halte.
Leider habe ich ein paar Fragen, zu der Funktionsweise, die ich hier einfach mal stelle:
1. Warum ist es gewünschtes Verhalten, dass man einmalig ein Tab mit der Tastatur ändern kann und dann nicht weiter?
2. Wie kann man testen ob die document-KeyUp-Listener funktionieren und was ist deren Funktion? (Die sind doch nur aktive, wenn der div-Container Fikus hat?)
Ich hoffe ich nerv nicht zu sehr rum, aber irgendwie muss man sowas ja lernen...
MFG Jan
1. Das Verhalten dass du beschreibst ist der focus wechsel nach einem click auf einen tab. Der focus wandert nach einem click in den aktiven tabinhalt anstatt auf dem tab zu verbleiben. Dies ist notwendig um Screenreader nutzern den ausgewählten Inhalt zu zeigen.In die Tabnavigation zurückzukehren kann man mit shift+tab. Dort funktioniert dann auch die cursor key navigation wieder
2. um key events zu testen lässt du dir am besten einen debug meldung in die konsole schreiben. Die events sollten and die root node der tab liste angebracht werden damit sie nur dort funktionieren.
english