Fokus in bestimmtem Website-Bereich fangen

Manchmal kann es sinnvoll sein, den Fokus in einem bestimmten Website-Bereich zu fangen, sodass kein fokussierbares Element außerhalb dieses Bereichs angesteuert werden kann (zum Beispiel über die Tab-Taste der Tastatur). Hier zeige ich, wie sich das umsetzen lässt.

Die Ausgangslage

Ich habe nach einer Lösung für das angesprochene Problem gesucht, als ich mein Fanoe-Theme überarbeitet habe. Das Theme kommt mit einer Off-Canvas-Sidebar, die seit der Überarbeitung nicht mehr den Inhalt mit nach links verschiebt, sondern darüber eingeblendet wird, wie in folgendem GIF zu sehen:

Wenn die Sidebar eingeblendet ist, soll der Fokus in der Sidebar gefangen sein, sodass ein Tastatur-User andere Fokus-Elemente außerhalb des Sidebar-Elements nicht ansteuern kann, bevor die Sidebar über den Button oder die Esc-Taste wieder ausgeblendet wird.

Umsetzung der Lösung mit ally.js

Die JavaScript-Bibliothek ally.js bietet eine Lösung für das Problem an und veranschaulicht die Funktionsweise in einem Tutorial. In meinem Theme sieht das entscheidende Markup so aus:

<button class="sidebar-button -open"> <span class="screen-reader-text">Show Sidebar</span> <span aria-hidden="true"></span> </button> <aside class="sidebar" role="sidebar"> <button class="sidebar-button -close"> <span class="screen-reader-text">Close Sidebar</span> <span aria-hidden="true"></span> </button> <div class="sidebar-content"> <!-- The widgets --> </div> </aside>

Und das ist der wichtige JS-Teil (ich nutze Webpack, um den import aufzulösen, und Babel, um ES6-Syntax in ES5 umzuwandeln – ich habe einen kleinen Beitrag zur Einrichtung davon geschrieben):

import ally from 'ally.js/ally'; /** * Custom JavaScript functions. * * @version 2.0.3 * * @package Fanoe */ { /** * Get the html element. * * @type {Element} */ const root = document.documentElement; /** * Get the elements for sidebar handling. */ const openButton = document.querySelector('.sidebar-button.-open'); const closeButton = document.querySelector('.sidebar-button.-close'); const sidebarElem = document.querySelector('.sidebar'); let disabledHandle; let tabHandle; let keyHandle; /** * Call sidebar function sidebar button click. */ openButton.addEventListener('click', sidebar, false); /** * Call sidebar function sidebar button click. */ closeButton.addEventListener('click', sidebar, false); /** * Catch clicks on the document to close sidebar on mouse click * outside the open sidebar. */ document.body.addEventListener('click', function (e) { /** * Check if the sidebar is visible. */ if (root.classList.contains('active-sidebar')) { /** * Check if the click was made on the .sidebar element, not the .sidebar-content. */ if (e.explicitOriginalTarget.classList.contains('sidebar')) { disabledHandle.disengage(); tabHandle.disengage(); root.classList.remove('active-sidebar'); } } }, false); /** * Function to display and hide sidebar. * * @link https://allyjs.io/tutorials/accessible-dialog.html */ function sidebar() { /** * Toggle .active-sidebar class. */ root.classList.toggle('active-sidebar'); /** * Check for class name to know if the * sidebar is currently visible or not. */ if (root.classList.contains('active-sidebar')) { disabledHandle = ally.maintain.disabled({ filter: sidebarElem, }); tabHandle = ally.maintain.tabFocus({ context: sidebarElem, }); keyHandle = ally.when.key({ escape: closeSidebarByKey, }); } else { disabledHandle.disengage(); tabHandle.disengage(); } } }

Der Code orientiert sich stark an dem oben verlinkten Tutorial. Zunächst importieren wir die ally-Bibliothek, holen das Root-Element und speichern die Buttons und das Sidebar-Element. Danach fügen wir die benötigten EventListener hinzu:

  • Klick auf die beiden Buttons. Davon wird die sidebar()-Funktion aufgerufen, um die Sidebar anzuzeigen oder auszublenden.
  • Klick auf das body-Element. Nach einem Klick wird geprüft, ob die Sidebar gerade angezeigt wird. In dem Fall wird danach geguckt, ob der Klick auf das .sidebar-Element ausgeführt wurde. Wenn das so ist, wurde der Klick auf den ausgegrauten Bereich außerhalb des Sidebar-Inhalts ausgeführt (das liegt daran, weil der graue Bereich über dem Rest der Website durch eine .sidebar::before-Regel erstellt wurde).

In sidebar() wird zunächst für das Root-Element die Klasse active-sidebar hinzugefügt oder entfernt – je nachdem, ob sie vorhanden ist oder nicht. Anschließend wird geprüft, ob die Klasse gerade vorhanden ist (die Sidebar also durch den Klick auf den Anzeigen-Button eingeblendet wurde) und in diesem Fall über ally.maintain.disabled allen Elementen außer denen innerhalb des Sidebar-Elements die Fokus-Möglichkeit entzogen. Über ally.maintain.tabFocus wird danach noch der Tab-Fokus auf die Sidebar gesetzt, damit nach dem letzten fokussierbaren Element der Sidebar nicht zum Browser-UI gesprungen wird, sondern zum ersten Fokus-Element der Sidebar.

Anschließend legen wir fest, dass bei Aufruf der Escape-Taste sidebar() aufgerufen werden soll, um die Sidebar zu schließen. Wenn active-sidebar nicht gesetzt ist, werden die ganzen Beschränkungen wieder rückgängig gemacht.

Das könnte auch interessant sein

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.