Библиотека ally.js предназначена для улучшения доступности веб-приложений, в том числе управления фокусом с клавиатуры. Управление стрелками — один из ключевых сценариев взаимодействия, позволяющий создавать логическую навигацию между интерактивными элементами, такими как списки, меню и сетки.
Для реализации управления стрелками используется комбинация функций focusable, tabbable, а также событий клавиатуры.
import * as ally from 'ally.js';
const elements = document.querySelectorAll('.menu-item');
// Определение всех элементов, которые могут быть сфокусированы
const focusableElements = ally.query.focusable({ context: document.querySelector('.menu') });
Ключевой момент: ally.query.focusable
возвращает массив всех элементов внутри указанного контекста, которые
могут принимать фокус. Это включает кнопки, ссылки, поля формы и
элементы с tabindex.
Для навигации между элементами списка необходимо отслеживать события клавиатуры:
document.querySelector('.menu').addEventListener('keydown', (event) => {
const active = document.activeElement;
const currentIndex = Array.prototype.indexOf.call(focusableElements, active);
switch(event.key) {
case 'ArrowDown':
event.preventDefault();
const nextIndex = (currentIndex + 1) % focusableElements.length;
focusableElements[nextIndex].focus();
break;
case 'ArrowUp':
event.preventDefault();
const prevIndex = (currentIndex - 1 + focusableElements.length) % focusableElements.length;
focusableElements[prevIndex].focus();
break;
}
});
Важное замечание: использование
event.preventDefault() предотвращает стандартное поведение
браузера, позволяя полностью контролировать фокус.
Для интерфейсов типа таблиц или сеток навигация стрелками требует расчета позиции элемента в двумерной структуре:
const rows = document.querySelectorAll('.grid-row');
function focusCell(rowIndex, colIndex) {
const cell = rows[rowIndex].children[colIndex];
if (cell) cell.focus();
}
document.querySelector('.grid').addEventListener('keydown', (event) => {
const activeCell = document.activeElement;
const rowIndex = Array.prototype.indexOf.call(rows, activeCell.parentElement);
const colIndex = Array.prototype.indexOf.call(rows[rowIndex].children, activeCell);
switch(event.key) {
case 'ArrowDown':
focusCell(rowIndex + 1, colIndex);
break;
case 'ArrowUp':
focusCell(rowIndex - 1, colIndex);
break;
case 'ArrowRight':
focusCell(rowIndex, colIndex + 1);
break;
case 'ArrowLeft':
focusCell(rowIndex, colIndex - 1);
break;
}
});
Особенность: такой подход позволяет реализовать навигацию не только по вертикали, но и по горизонтали, создавая полный контроль над перемещением фокуса.
При работе с меню и списками часто требуется, чтобы фокус циклически возвращался к началу или концу:
function getNextIndex(currentIndex, step, length) {
return (currentIndex + step + length) % length;
}
Использование этой функции позволяет легко обходить элементы без выхода за пределы массива. Это важно для создания доступных и интуитивных интерфейсов.
ally.js поддерживает динамические изменения DOM. Если список элементов обновляется после загрузки страницы, нужно пересчитывать фокусируемые элементы:
function updateFocusableElements() {
return ally.query.focusable({ context: document.querySelector('.menu') });
}
let focusableElements = updateFocusableElements();
const observer = new MutationObserver(() => {
focusableElements = updateFocusableElements();
});
observer.observe(document.querySelector('.menu'), { childList: true, subtree: true });
Ключевой момент: использование
MutationObserver гарантирует актуальность массива
фокусируемых элементов при добавлении или удалении элементов в DOM.
ally.js позволяет управлять порядком фокуса через атрибуты
tabindex:
tabindex="0" — элемент включается в стандартный порядок
фокуса.tabindex="-1" — элемент может быть сфокусирован
программно, но не через табуляцию.tabindex изменяют приоритет
фокуса.Комбинация программного управления стрелками и правильного назначения
tabindex позволяет создать сложные, доступные интерфейсы с
полной клавиатурной навигацией.
focusable только при изменении
DOM: кэширование результатов уменьшает вычислительные
затраты.:focus стили).Эти подходы обеспечивают создание доступных и логичных интерфейсов, где управление стрелками полностью контролируется разработчиком и учитывает динамику DOM.