Для работы с ally.js в проекте на React необходимо сначала установить библиотеку через npm или yarn:
npm install ally.js
# или
yarn add ally.js
После установки можно импортировать необходимые функции из библиотеки:
import ally from 'ally.js';
ally.js не зависит от конкретного фреймворка, что делает её удобной для использования в React-компонентах. Однако важно помнить о синхронизации с жизненным циклом компонентов, чтобы не оставлять активные обработчики после размонтирования.
ally.js ориентирована на улучшение доступности веб-приложений, предоставляя инструменты для работы с фокусом, выделением элементов и проверкой доступности. Основные направления:
ally.js предоставляет метод nextTabbable и
previousTabbable для поиска следующего или предыдущего
интерактивного элемента в DOM:
const nextElement = ally.query.nextTabbable(document.activeElement, {
filter: '[data-focusable="true"]'
});
const focusableElements = ally.query.tabbable(document.body);
Этот метод возвращает массив элементов, которые можно сфокусировать клавишей Tab. В React это удобно использовать при создании модальных окон или навигационных меню, чтобы управлять фокусом внутри компонента.
Для контроля фокуса в функциональных компонентах применяются хуки
useEffect и useRef:
import { useEffect, useRef } from 'react';
import ally from 'ally.js';
function Modal({ isOpen }) {
const modalRef = useRef(null);
useEffect(() => {
if (isOpen && modalRef.current) {
const firstFocusable = ally.query.firstTabbable(modalRef.current);
firstFocusable && firstFocusable.focus();
}
}, [isOpen]);
return (
<div ref={modalRef} role="dialog" aria-modal="true">
<button>Закрыть</button>
<input type="text" placeholder="Имя" />
</div>
);
}
Ключевые моменты:
useRef.firstTabbable,
lastTabbable и nextTabbable для
последовательного управления фокусом.Focus Trap — это сценарий, когда пользовательский фокус остаётся внутри определённого блока, например, модального окна. ally.js упрощает его реализацию:
import ally from 'ally.js';
function trapFocus(container) {
const handleKeyDown = (event) => {
if (event.key !== 'Tab') return;
const tabbable = ally.query.tabbable(container);
if (!tabbable.length) return;
const first = tabbable[0];
const last = tabbable[tabbable.length - 1];
if (event.shiftKey && document.activeElement === first) {
last.focus();
event.preventDefault();
} else if (!event.shiftKey && document.activeElement === last) {
first.focus();
event.preventDefault();
}
};
container.addEventListener('keydown', handleKeyDown);
return () => container.removeEventListener('keydown', handleKeyDown);
}
В React это можно обернуть в useEffect для безопасного
подключения и отключения обработчика:
useEffect(() => {
if (!isOpen || !modalRef.current) return;
const cleanup = trapFocus(modalRef.current);
return cleanup;
}, [isOpen]);
ally.js предоставляет методы для проверки доступности элементов:
const accessibilityCheck = ally.tools.toString(document.querySelector('button'));
console.log(accessibilityCheck);
Метод toString возвращает текстовое описание проблем,
связанных с ARIA-атрибутами, видимостью и фокусируемостью. Это полезно
для динамических компонентов, где контент меняется на
лету.
В React часто встречаются асинхронные обновления DOM. ally.js позволяет безопасно находить элементы после рендеринга:
useEffect(() => {
const timeout = setTimeout(() => {
const firstInput = ally.query.firstTabbable(document.body);
firstInput && firstInput.focus();
}, 0);
return () => clearTimeout(timeout);
}, []);
Такой подход гарантирует, что элементы уже присутствуют в DOM перед установкой фокуса.
При работе с списками, которые подгружаются асинхронно или изменяются через состояние React, ally.js помогает управлять фокусом без лишних манипуляций:
const itemsRef = useRef([]);
const items = ['Элемент 1', 'Элемент 2', 'Элемент 3'];
useEffect(() => {
const lastItem = ally.query.lastTabbable(document.getElementById('list'));
lastItem && lastItem.focus();
}, [items]);
Преимущества:
button,
input, a с href).Важно возвращать фокус на исходный элемент после закрытия диалога:
const triggerRef = useRef(null);
function openModal() {
triggerRef.current && triggerRef.current.focus();
}
В сочетании с ally.query.firstTabbable это обеспечивает
полную цикличность навигации клавиатурой и соответствие стандартам
доступности.
ally.js в React обеспечивает гибкий, надёжный и удобный способ управления фокусом, упрощает работу с ARIA-атрибутами и гарантирует доступность компонентов без ручного контроля всех интерактивных элементов. Она особенно полезна для модальных окон, меню, динамических форм и списков, где управление фокусом вручную становится громоздким и подверженным ошибкам.