Verstecken von Inhalten entwirrt: Verstecken vs. aus sichtbarem Bereich verschieben

Der Anlass für diesen Artikel war mal wieder ein Beispiel, das aus dem Leben gegriffen ist. Ich testete am Montag browserid.org auf Barrierefreiheit und stieß auf einige Ungereimtheiten bei der Tastatur- und ScreenReader-Bedienung. Zum einen gibt es sowohl oberhalb der zu einem Konto gehörigen E-Mail-Adressen als auch neben der Überschrift zum Ändern des Passworts „Edit“-Buttons, deren Funktionalität sich mir nicht erschließen wollte. Denn für meinen Screen Reader waren die „Remove“-Buttons neben den E-Mail-Adressen bzw. die Felder fürs alte und neue Passwort auch ohne Betätigen dieser „Edit“-Buttons sichtbar, und die Aktionen konnten ohne weiteres ausgeführt werden. Zum anderen erschloss sich mir auch beim Durchnavigieren der Seite mit Tab nicht, welchen Unterschied diese „Edit“-Buttons machen sollten.

Die Lösung ergab sich erst, als ich meine Lebensgefährtin bat, sich diese Seite mit mir zusammen anzuschauen. Für sie war nach dem ersten Laden keiner der „Remove“-Buttons oder die Eingabefelder für die Passwörter sichtbar. Als ich anfing, mit dem virtuellen Cursor von NVDA hindurchzunavigieren, erschienen plötzlich die Buttons und Eingabefelder. Wanderte ich rückwärts, verschwanden sie wieder. Navigierte ich mit Tab, erschienen sie und blieben sichtbar.

Das „Verstecken“ der Inhalte wurde hier also auf eine Weise erledigt, die die Inhalte zwar für Sehende versteckte, für Screen Reader aber nicht. Weiterhin wurde nicht berücksichtigt, dass sich für Tastaturbenutzer ein anderes Interaktionsmodell ergibt als für Mausbenutzer. Es wurde die falsche Technik zum Verstecken von Inhalten angewandt.

Ich will mal versuchen, ein bisschen Licht ins Dunkel zu bringen und gleichzeitig ein paar grundsätzliche Missverständnisse aufzuklären, mit denen ich seit über 10 Jahren immer wieder konfrontiert werde.

Die Grundlage

Zum Verständnis unbedingt erforderlich ist das Folgende: Screen Reader und andere assistive Technologien für Menschen mit Behinderungen nutzen eine Sammlung von Objekten, die den Inhalt einer Webseite widerspiegelt, um dem Anwender den Zugang hierzu ermöglichen. Diese Objekte sind hierarchisch gegliedert, wie in einem Baum. Der Stamm ist das Dokument, die Kindelemente sind vielleicht einzelne Sektionen, Überschriften, navigationsblöcke usw. Dieser Baum stellt eine Untermenge des durch den HTML-Code der Seite erstellten Baums des Document Object Model (DOM) des Browsers dar. Da die Objekte für Barrierefreiheit nicht für jedes Element eine Entsprechung benötigen, ist die Abbildung nicht ganz 1:1. In Fachkreisen wird diese Untermenge als Accessible Tree bezeichnet. Die Browser-Engine, z. B. Gecko bei Mozilla-basierten Browsern, versieht diese Objekte jedoch mit weiteren Eigenschaften, die für den Menschen mit Behinderung unbedingt notwendig sind.

Hierzu werden nicht nur die Informationen aus den Attributen des jeweiligen HTML-Tags herangezogen, sondern auch Stilinformationen. So werden Informationen zu Textattributen aus dem CSS abgeleitet, Farben usw. ebenfalls daraus ermittelt.

Einige Eigenschaften werden nicht berücksichtigt, z. B. die letztendliche Positionierung einzelner Elemente. Ein Div-Container kann also z. B. auf der Startseite oben rechts in der Ecke neben der Navigation angezeigt werden, der Screen Reader wird ihn jedoch an der Stelle des Dokumentflusses anzeigen, an dem er im Quelltext steht. Die Positionierung per CSS hat auf die Lesereihenfolge des Screen Readers keinen Einfluss. Siehe hierzu auch diesen Eintrag von mir, in dem ich das am Beispiel einer Lightbox erkläre.

Verstecken ist nicht gleich verstecken

Einige CSS-Anweisungen werden jedoch strikt befolgt. Im IE macht dies der Screen Reader, bei Firefox macht es die Gecko-Engine schon vorab. Und dies ist schon seit über 10 Jahren so. Elemente, die mit „display: none;“ oder „visibility: hidden;“ gestylt werden, werden nicht mit in den Accessible Tree aufgenommen. Erst wenn sich z. B. durch eine JavaScript-Anweisung ihr Styling ändert, werden sie an entsprechender Stelle in den Baum eingefügt, und es wird ein Ereignis ausgelöst, das dem Screen Reader mitteilt, dass hier ein neues Element oder eine Gruppe von Elementen erschienen ist.

Andere Anweisungen zum „Verstecken“ von Elementen wie „left: -9999px; top: -9999px;“ werden hingegen nicht berücksichtigt. Diese Elemente sind für Screen Reader genauso sichtbar wie solche, als stünden sie sichtbar fürs Auge auf dem Bildschirm.

Also nochmal, damit’s niemand überliest: Elemente, die mit „display: none;“ oder „visibility: hidden;“ gestylt sind, werden auch vor Screen Reedern immer versteckt! Dies gilt ohne Ausnahme für NVDA, JAWS usw. unter Windows, Orca unter Linux, und VoiceOver auf Mac und iOS. Die letzte JAWS-Version, die das nicht konnte, war, glaube ich, Version 4.02, die im April oder Mai 2001 erschien. Seitdem machen das alle alle alle JAWS-Versionen und alle anderen Screen Reader, die ich kenne, richtig. Warum sich ein das Gegenteil behauptendes Gerücht bis heute so hartnäckig in der Webentwickler-Szene hält, ist mir ein absolutes Rätsel. „display: none;“ oder „visibility: hidden;“ eigenen sich also nicht, um dem Screen Reader Zusatzinhalte anzubieten. Er wird sie schnöde ignorieren!

Wann verwende ich denn nun was?

Sobald Elemente tatsächlich erst sichtbar werden sollen, wenn eine bestimmte Aktion bewusst ausgeführt wird, sollten, je nach Bedarf, „display: none;“ oder „visibility: hidden;“ verwendet werden. Ein Anwendungsbeispiel, das viele Blogger jedesmal vor der Nase haben, wenn sie selbst einen Beitrag schreiben, ist das Administrations-Backend von WordPress. Die Hauptmenüpunkte „Dashboard“, „Artikel“ usw. verbergen ihre Untermenüs durch das Stylen mit einer dieser beiden CSS-Anweisungen. Erst wenn der Anwender entweder mit der Maus rüberfährt oder mit Tab drauf navigiert und Enter drückt, wird der Style entsprechend geändert, und die Untermenüpunkte erscheinen. Richtigerweise wird auf dem Hauptmenüpunkt auch das WAI-ARIA-Attribut aria-haspopup=“true“ wird richtig gesetzt, so dass man sofort mitgeteilt bekommt, dass hier ein Untermenü vorliegt.

Würde hier mit einem lediglich aus dem sichtbaren Bereich verschobenen Container gearbeitet, würde ich die ganzen Untermenü-Punkte mit meinem Screen Reader zu sehen bekommen. Sämtliche Vorteile eines aufgeräumteren Interfaces wären für mich dahin. Ja, auch ich genieße die Vorzüge dynamischer Interfaces, in denen ich ein- und ausblenden kann, was ich zur zeit zum Arbeiten brauche und was nicht! Ist das entsprechende Markup vorhanden, muss ich nicht zwingend sofort sämtlichen verfügbaren Inhalt sehen. Der macht auch für mich ein Interface unaufgeräumt, unübersichtlich und schwer navigierbar!

Der falsche Weg ist der, den ich am Anfang des Artikels beschrieben habe. Die Buttons und Passwort-Felder im Kontobereich von BrowserID wurden lediglich aus dem sichtbaren Bereich verschoben, nicht wirklich versteckt.

Eine sinnvolle Anwendung von aus dem sichtbaren Bereich verschobenen Elementen ist die Verwendung sogenannter Skip Links. Dirk Jesse hat hierzu einen vorzüglichen Blogeintrag verfasst, dessen Lektüre ich dem geneigten Leser wärmstens ans Herz legen möchte! Auch dort wird kurz darauf verwiesen, wofür „display: none;“ der völlig falsche Weg ist.

Fazit

Ich hoffe, dieser Artikel kann dazu beitragen, einige Verwirrung aufzulösen, die um die Verwendung von „display: none;“ und negativer Positionierung besteht, wann was angewendet werden sollte und wann nicht. Die Faustregel ist: Soll etwas komplett versteckt und erst nach einer bewussten Aktion eingeblendet werden, „echt“ verstecken. Soll etwas grundsätzlich für Screen Reader erreichbar sein, ohne dass man vorher eine Aktion ausführen muss, dieses Element aber nicht zwangsläufig sofort sichtbar sein, verwendet man negative Positionierung. Aber dabei auch immer schön an die Tastaturbenutzer ohne Screen Reader denken und die Elemente rechtzeitig einblenden! 😉

Fragen? Die gibt es bestimmt! Immer her damit, denn nur „wer nicht fragt, bleibt dumm“, das wissen wir ja schon seit der Sesamstraße! 🙂

7 Gedanken zu „Verstecken von Inhalten entwirrt: Verstecken vs. aus sichtbarem Bereich verschieben“

  1. Gerade bei Skip Links fällt auf, dass wohl gerade große Agenturen oft nicht wissen, was sie tun. Eine große (nicht die einzige, ich weiß) Zielgruppe für Skip Links sind blinde Nutzer von Screen Reader. Wer den Skip Link mit display:none versteckt, hätte sich die Mühe sparen können, einen Skip Link einzubauen. Das habe ich bereits 2009 in einem kurzen Blogartikel moniert. Vergeblich. Einer http://www.zdf.de stünde ein funktionierender Skip Link sehr wohl zu Gesicht. Viele weitere Beispiele erspare ich mir. Ebenso erspare ich mir inzwischen, was ich über Jahre uneigennützig praktiziert habe: Die Seitenbetreiber bzw. die Agenturen auf dieses Manko hinzuweisen. Ich hatte mit meinen entsprechenden Hinweisen an überwiegend „große Adressen“ nämlich genau 0% Erfolg. Nicht eine einzige Antwort, geschweige denn eine Korrektur des Fehlers.

  2. Danke für die Ausarbeitung. Ich habe noch ein Paar Anmerkungen zu display/visibility:

    * Der gängige Screenreader JAWS hat erst ab der Version 7 die beiden Eigenschaften konsequent für den Screenreader versteckt.
    * Beispielsweise die Version 6 des Screenreaders JAWS hat display und visibility nicht für den Screenreader versteckt.

    Außer der genannten Technik, solche Inhalte aus dem iewport zu schieben, die für Screenreader zu erfassen sein sollen (eine zuverlässige Methode), findet man heute auch zahlreiche andere Techniken zum Verstecken von Inhalten wie der Einsatz der CSS-Eigenschaft clip.

    Insgesamt kann man den konsequenten Einsatz der „richtigen“ CSS-Techniken zwar nur begrüßen, aber es wäre aus meiner Sicht besser, wenn solche Inhalte, die (zeitweise) nicht Teil des UI sind, als Knoten aus dem DOM entfernt würden und bei Bedarf wieder eingefügt. Das ist eine Herausforderung an die JS-Bibliotheken.

  3. @Jan Eric,

    es wäre aus meiner Sicht besser, wenn solche Inhalte, die (zeitweise) nicht Teil des UI sind, als Knoten aus dem DOM entfernt würden und bei Bedarf wieder eingefügt. Das ist eine Herausforderung an die JS-Bibliotheken.

    Und es ist ein Problem für die meisten Screen reader. Was als Knoten entfernt ist, wird auch nicht angezeigt. Man weiß also nie, dass ein Skip Link da ist. Und nicht alle Screen Reader verschieben beim Navigieren mit dem virtuellen Cursor den Tastaturfokus. JAWS zum Beispiel nur dann, wenn man die entsprechende Option zuschaltet. Der Effekt wäre also der gleiche wie das Verstecken durch „display: none;“.

  4. Ich wäre vermutlich im Leben nicht auf die Idee gekommen, dafür aria-haspopup einzusetzen, hätte vermutlich eher etwas mit aria-hidden gebaut. Oder macht das für Screenreader letztlich praktisch keinen Unterschied?

  5. @Marco:

    > Und es ist ein Problem für die meisten Screen reader. Was als Knoten entfernt ist, wird auch nicht angezeigt. Man weiß also nie, dass ein Skip Link da ist.

    Das bezog sich nicht auf die Skip-Links. Dass sie per CSS versteckt werden sollten, ist keine Frage. Dein Artikel begann mit einem Beispiel mit Schaltflächen, die am Bildschirm nicht sichtbar waren und im Screenreader aber schon. Solche Teile, die zeitweise im UI nicht genutzt werden, gehören m.E. nicht ins DOM.

  6. Solche Teile, die zeitweise im UI nicht genutzt werden, gehören m.E. nicht ins DOM.

    Nun, ein Element neu zu erzeugen und einzufügen ist ein größerer Performance-Fresser als das sichtbar machen eines solchen Elements durch die Änderung des Stylings. Keine Ahnung, ob sich das durchsetzt. Weiterhin gibt es dann natürlich ein Problem mit der Referenzierbarkeit. Ein Element, das nicht im DOM ist, kann nicht referenziert werden. Und UI-Elemente wie eine ganze Latte von Untermenüeinträgen ständig neu zu erzeugen und wieder wegzunehmen, kann schon ganz schön auf die Performance schlagen.

  7. @Marco:

    OK, die Knoten müssen nicht gelöscht werden, sondern können z.B. mit appendChild irgendwo „abgelegt“ werden, aber nicht unter dem HTML-Wurzelelement. Ein Zurückschieben geht nach meiner Erfahrung sehr fix, aber ich habe das nicht gemessen.

Was denkst Du darüber?