Muuri — это высокоэффективная библиотека для создания сеточных интерфейсов с динамическим позиционированием элементов. Помимо перетаскивания мышью, важным аспектом взаимодействия является навигация с клавиатуры, которая обеспечивает доступность интерфейса и удобство работы без мыши.
В Muuri навигация с клавиатуры реализуется через фокус на
элементах сетки и обработку событий клавиш. Каждый элемент
сетки может получать фокус с помощью стандартных HTML-атрибутов
tabindex и методов JavaScript.
const grid = new Muuri('.grid', {
dragEnabled: true,
layoutOnInit: true
});
const items = grid.getItems();
items.forEach(item => {
item.getElement().setAttribute('tabindex', '0');
});
В приведённом примере каждому элементу присваивается
tabindex="0", что делает его доступным для навигации через
клавишу Tab.
Для управления сеткой с клавиатуры используется событие
keydown. Его можно привязать как к отдельным элементам, так
и к документу в целом.
grid.getItems().forEach(item => {
const el = item.getElement();
el.addEventListener('keydown', event => {
switch(event.key) {
case 'ArrowUp':
moveFocus(item, 'up');
break;
case 'ArrowDown':
moveFocus(item, 'down');
break;
case 'ArrowLeft':
moveFocus(item, 'left');
break;
case 'ArrowRight':
moveFocus(item, 'right');
break;
}
});
});
Функция moveFocus отвечает за определение следующего
элемента в указанном направлении и передачу ему фокуса.
Для корректной навигации необходимо вычислить расположение элементов
сетки. Muuri предоставляет методы getItems() и
getElement() для работы с элементами.
Пример функции для перемещения фокуса:
function moveFocus(currentItem, direction) {
const items = grid.getItems();
const currentRect = currentItem.getElement().getBoundingClientRect();
let targetItem = null;
let minDistance = Infinity;
items.forEach(item => {
if (item === currentItem) return;
const rect = item.getElement().getBoundingClientRect();
let distance = Infinity;
switch(direction) {
case 'up':
if (rect.bottom <= currentRect.top) distance = currentRect.top - rect.bottom;
break;
case 'down':
if (rect.top >= currentRect.bottom) distance = rect.top - currentRect.bottom;
break;
case 'left':
if (rect.right <= currentRect.left) distance = currentRect.left - rect.right;
break;
case 'right':
if (rect.left >= currentRect.right) distance = rect.left - currentRect.right;
break;
}
if (distance < minDistance) {
minDistance = distance;
targetItem = item;
}
});
if (targetItem) targetItem.getElement().focus();
}
Эта функция выбирает ближайший элемент в заданном направлении и передаёт ему фокус, обеспечивая интуитивную навигацию по сетке.
Если включён dragEnabled, навигация клавиатурой должна
корректно взаимодействовать с перетаскиванием. Muuri предоставляет
события dragStart, dragEnd и
move, которые можно использовать для синхронизации
состояния элементов.
Пример приостановки фокуса во время перетаскивания:
grid.on('dragStart', item => {
item.getElement().setAttribute('tabindex', '-1');
});
grid.on('dragEnd', item => {
item.getElement().setAttribute('tabindex', '0');
});
Это предотвращает случайное получение фокуса на элементе, который перемещается.
tabindex только для элементов, доступных
для навигации.role="gridcell",
чтобы экранные читалки корректно интерпретировали сетку.tabindex и привязывать обработчики клавиатуры.const grid = new Muuri('.grid', { dragEnabled: true, layoutOnInit: true });
function setupKeyboardNavigation() {
grid.getItems().forEach(item => {
const el = item.getElement();
el.setAttribute('tabindex', '0');
el.addEventListener('keydown', event => {
switch(event.key) {
case 'ArrowUp': moveFocus(item, 'up'); break;
case 'ArrowDown': moveFocus(item, 'down'); break;
case 'ArrowLeft': moveFocus(item, 'left'); break;
case 'ArrowRight': moveFocus(item, 'right'); break;
case 'Enter': activateItem(item); break;
}
});
});
}
function activateItem(item) {
console.log('Активирован элемент:', item.getElement());
}
setupKeyboardNavigation();
grid.on('add', () => setupKeyboardNavigation());
grid.on('dragStart', item => item.getElement().setAttribute('tabindex', '-1'));
grid.on('dragEnd', item => item.getElement().setAttribute('tabindex', '0'));
Данный подход позволяет сочетать доступность, удобство навигации и drag-and-drop функциональность в одной сетке Muuri.
Хорошо продуманная навигация с клавиатуры повышает юзабилити и делает интерфейс полностью доступным для пользователей с различными способностями.