Versehentlich veröffentlicht! Bitte kommen Sie später noch einmal vorbei, um mehr zu erfahren!
Das Erstellen barrierefreier Webanwendungen ist nicht nur eine gute Praxis – es ist jetzt eine Notwendigkeit. Kürzlich hatte ich die Gelegenheit, eine Navigationsmenüleiste mit Schwerpunkt auf a11y zu erstellen. Bei meinen Recherchen wurde mir klar, dass die meisten Menüleisten nicht dem ARIA-Muster entsprechen. Wussten Sie beispielsweise, dass eine Menüleiste mit Pfeiltasten navigiert werden und ihren eigenen Fokus verwalten sollte, anstatt durch Menüelemente zu scrollen?
Obwohl ich einige Tutorials gefunden habe, habe ich sie letztendlich nicht vollständig befolgt. Ich schreibe dies, weil ich denke, dass das, was ich letztendlich gebaut habe, es wert ist, geteilt zu werden – wenn Sie auch eine Affinität zu kleinen Komponenten und benutzerdefinierten Hooks haben.
Obwohl ich diesen Blog mit einigen Entwicklungsschritten strukturieren werde, ist es nicht mein Ziel, eine Schritt-für-Schritt-Anleitung zu schreiben. Ich vertraue darauf, dass Sie die Grundlagen von React kennen und wissen, wie benutzerdefinierte Hooks funktionieren.
Ich teile jetzt nur die wichtigsten Implementierungsdetails, habe aber vor, diesen Artikel in Zukunft mit einem Code-Sandbox-Beispiel zu aktualisieren, wenn ich mehr Zeit habe.
Für diesen Blog entwickeln wir eine Navigationsmenüleiste, wie Sie sie oben oder an der Seite vieler Webanwendungen sehen. In dieser Menüleiste können einige Menüelemente Untermenüs haben, die sich beim Betreten/Verlassen mit der Maus öffnen/schließen.
In erster Linie sind semantisches HTML und entsprechende Rollen und ARIA-Attribute für die Barrierefreiheit unerlässlich. Weitere Informationen zum Menüleistenmuster finden Sie im offiziellen Dokument hier.
Hier ist ein Beispiel für ein geeignetes HTML-Markup:
Beachten Sie, dass wir das Button-Tag für semantisches HTML verwenden. Die Schaltfläche sollte auch über aria-haspopup verfügen, um Screenreader darauf aufmerksam zu machen. Abschließend sollte je nach Menüstatus das entsprechende aria-expanded-Attribut zugewiesen werden.
Lassen Sie uns die Komponenten durchgehen, die wir benötigen. Offensichtlich benötigen wir eine allgemeine Menükomponente sowie eine Menüelementkomponente.
Einige Menüpunkte haben ein Untermenü, andere nicht. Die Menüelemente mit Untermenüs müssen ihren Status für das Öffnen/Schließen von Untermenüs bei Hover- und Tastaturereignissen verwalten. Es muss also eine eigene Komponente sein.
Untermenüs müssen ebenfalls eine eigene Komponente sein. Obwohl Untermenüs auch nur Container für Menüelemente sind, verwalten sie weder deren Status noch verarbeiten sie Tastaturereignisse. Dies unterscheidet sie vom Navigationsmenü der obersten Ebene.
Am Ende habe ich diese Komponenten geschrieben:
Ganz einfach ausgedrückt bedeutet „Fokusverwaltung“ lediglich, dass die Komponente wissen muss, welches Kind den Fokus hat. Wenn also der Fokus des Benutzers verschwindet und wieder zurückkehrt, wird das zuvor fokussierte Kind wieder fokussiert.
Eine gängige Technik zur Fokusverwaltung ist der „Roving Tab Index“, bei dem das fokussierte Element in der Gruppe einen Tab-Index von 0 und andere Elemente einen Tab-Index von -1 haben. Wenn der Benutzer auf diese Weise zur Fokusgruppe zurückkehrt, hat das Element mit dem Tabulatorindex 0 automatisch den Fokus.
Eine erste Implementierung für NavMenu kann etwa so aussehen:
export function NavMenu ({ menuItems }) { // state for the currently focused index const [focusedIndex, setFocusedIndex] = useState(0); // functions to update focused index const goToStart = () => setCurrentIndex(0); const goToEnd = () => setCurrentIndex(menuItems.length - 1); const goToPrev = () => { const index = currentIndex === 0 ? menuItems.length - 1 : currentIndex - 1; setCurrentIndex(index); }; const goToNext = () => { const index = currentIndex === menuItems.length - 1 ? 0 : currentIndex 1; setCurrentIndex(index); }; // key down handler according to aria specification const handleKeyDown = (e) => { e.stopPropagation(); switch (e.code) { case "ArrowLeft": case "ArrowUp": e.preventDefault(); goToPrev(); break; case "ArrowRight": case "ArrowDown": e.preventDefault(); goToNext(); break; case "End": e.preventDefault(); goToEnd(); break; case "Home": e.preventDefault(); goToStart(); break; default: break; } } return ( ); }
Die Funktion e.preventDefault() dient dazu, Dinge wie das Scrollen mit ArrowDown auf der Seite zu verhindern.
Hier ist die MenuItem-Komponente. Lassen Sie uns die Elemente mit Untermenü für eine Sekunde ignorieren. Wir verwenden useEffect, usePrevious und element.focus(), um den Fokus auf das Element zu richten, wann immer sich der focusIndex ändert:
export function MenuItem ({ item, index, focusedIndex, setFocusedIndex }) { const linkRef = useRef(null); const prevFocusedIndex = usePrevious(focusedIndex); const isFocused = index === focusedIndex; useEffect(() => { if (linkRef.current && prevFocusedIndex !== currentIndex && isFocused) { linkRef.current.focus() } }, [isFocused, prevFocusedIndex, focusedIndex]); const handleFocus = () => { if (focusedIndex !== index) { setFocusedIndex(index); } }; return (
Beachten Sie, dass es sich um das Tag „a“ handelt, das die Referenz (Schaltfläche für Menüelement mit Untermenüs) haben sollte. Wenn Sie also darauf fokussiert sind, werden die Standard-Tastaturverhaltensweisen wie erwartet aktiviert, z. B. die Navigation bei der Eingabetaste. Darüber hinaus wird der Tab-Index je nach fokussiertem Element richtig zugewiesen.
Wir fügen einen Ereignishandler für Fokusereignisse hinzu, falls das Fokusereignis nicht von einem Tasten-/Mausereignis stammt. Hier ist ein Zitat aus dem Webdokument:
Gehen Sie nicht davon aus, dass alle Fokusänderungen über Tasten- und Mausereignisse erfolgen: Hilfstechnologien wie Bildschirmleseprogramme können den Fokus auf jedes fokussierbare Element setzen.
Wenn Sie dem oben beschriebenen useEffect folgen, werden Sie feststellen, dass das erste Element den Fokus hat, auch wenn der Benutzer nicht die Tastatur zum Navigieren verwendet hat. Um dies zu beheben, können wir das aktive Element überprüfen und focus() nur aufrufen, wenn der Benutzer ein Tastaturereignis gestartet hat, wodurch der Fokus vom Körper weg verschoben wird.
useEffect(() => { if (linkRef.current && document.activeElement !== document.body // only call focus when user uses keyboard navigation && prevFocusedIndex !== focusedIndex && isCurrent) { linkRef.current.focus(); } }, [isCurrent, focusedIndex, prevFocusedIndex]);
Bisher verfügen wir über funktionsfähige NavMenu- und MenuItemLink-Komponenten. Kommen wir zum Menüpunkt mit Untermenüs.
Als ich es schnell ausarbeitete, wurde mir klar, dass dieser Menüpunkt den Großteil der Logik teilen wird
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3