Packery — это библиотека для создания динамических сеток с возможностью перетаскивания элементов. Несмотря на то, что изначально фокус сделан на мыши и drag-and-drop, правильная интеграция навигации с клавиатуры делает сетку более доступной и удобной для пользователей с различными потребностями. Основной принцип заключается в том, чтобы элементы сетки можно было выбирать, перемещать и взаимодействовать с ними с помощью стандартных клавиш: стрелок, Tab, Enter и модификаторов.
Для клавиатурной навигации каждый элемент сетки должен быть
фокусируемым. Это достигается добавлением атрибута
tabindex="0" к элементам, которые будут интерактивными:
<div class="grid">
<div class="grid-item" tabindex="0">Элемент 1</div>
<div class="grid-item" tabindex="0">Элемент 2</div>
<div class="grid-item" tabindex="0">Элемент 3</div>
</div>
Фокусировка позволяет пользователю перемещаться между элементами с
помощью клавиши Tab. После установки фокуса можно
реагировать на нажатия стрелок для перемещения или изменения положения
элемента.
Для управления сеткой через клавиатуру используется метод
addEventListener на каждом элементе. Основные события:
keydown — отслеживает нажатие клавиш;focus — отслеживает фокусировку элемента.Пример обработки стрелок для перемещения элементов:
const grid = document.querySelector('.grid');
const pckry = new Packery(grid, {
itemSelector: '.grid-item',
gutter: 10
});
grid.addEventListener('keydown', (event) => {
const item = event.target;
if (!item.classList.contains('grid-item')) return;
let deltaX = 0, deltaY = 0;
switch(event.key) {
case 'ArrowUp': deltaY = -1; break;
case 'ArrowDown': deltaY = 1; break;
case 'ArrowLeft': deltaX = -1; break;
case 'ArrowRight': deltaX = 1; break;
case 'Enter':
pckry.fit(item, item.offsetLeft, item.offsetTop);
break;
}
if (deltaX || deltaY) {
moveItemWithKeyboard(item, deltaX, deltaY);
}
});
function moveItemWithKeyboard(item, dx, dy) {
const style = window.getComputedStyle(item);
const currentLeft = parseInt(style.left, 10) || 0;
const currentTop = parseInt(style.top, 10) || 0;
item.style.left = currentLeft + dx * 20 + 'px';
item.style.top = currentTop + dy * 20 + 'px';
pckry.layout();
}
Для более сложного взаимодействия удобно использовать
клавиши-модификаторы (Shift, Ctrl,
Alt) вместе со стрелками. Например:
Пример интеграции модификатора Shift:
const step = 20;
const bigStep = 60;
function moveItemWithKeyboard(item, dx, dy, big = false) {
const size = big ? bigStep : step;
const style = window.getComputedStyle(item);
const currentLeft = parseInt(style.left, 10) || 0;
const currentTop = parseInt(style.top, 10) || 0;
item.style.left = currentLeft + dx * size + 'px';
item.style.top = currentTop + dy * size + 'px';
pckry.layout();
}
grid.addEventListener('keydown', (event) => {
const big = event.shiftKey;
switch(event.key) {
case 'ArrowUp': moveItemWithKeyboard(event.target, 0, -1, big); break;
case 'ArrowDown': moveItemWithKeyboard(event.target, 0, 1, big); break;
case 'ArrowLeft': moveItemWithKeyboard(event.target, -1, 0, big); break;
case 'ArrowRight': moveItemWithKeyboard(event.target, 1, 0, big); break;
}
});
После изменения позиции элемента важно сохранять
фокус на нем, иначе пользователь потеряет ориентацию в сетке.
Для этого достаточно вызывать element.focus() после
завершения перемещения:
function moveItemWithKeyboard(item, dx, dy, big = false) {
const size = big ? bigStep : step;
const style = window.getComputedStyle(item);
const currentLeft = parseInt(style.left, 10) || 0;
const currentTop = parseInt(style.top, 10) || 0;
item.style.left = currentLeft + dx * size + 'px';
item.style.top = currentTop + dy * size + 'px';
pckry.layout();
item.focus();
}
Packery совместим с Draggabilly и jQuery UI Draggable. Для клавиатурной навигации это позволяет объединять методы:
dragEnd позволяет обновлять сетку после
перемещения клавишами.const draggies = [];
pckry.getItemElements().forEach(function(itemElem) {
const draggie = new Draggabilly(itemElem);
draggies.push(draggie);
itemElem.addEventListener('keydown', (event) => {
if (event.key === 'Enter') draggie.enable();
if (event.key === 'Escape') draggie.disable();
});
});
Клавиатурная навигация должна учитывать ARIA-атрибуты:
role="grid" для контейнера;role="gridcell" для элементов;aria-selected="true/false" для выделенных
элементов;aria-grabbed="true/false" при перемещении.<div class="grid" role="grid">
<div class="grid-item" role="gridcell" tabindex="0" aria-selected="false">Элемент 1</div>
</div>
Это позволяет не только клавиатурное управление, но и взаимодействие с программами чтения с экрана, делая сетку полностью доступной.
tabindex="0").keydown.Такой подход обеспечивает полное управление сеткой Packery с клавиатуры без потери функциональности drag-and-drop и без снижения доступности.