Клавиатурная навигация в веб-картах обеспечивает доступность интерфейса без использования мыши и критически важна для соответствия стандартам WCAG. В контексте Mapbox GL JS она реализуется через механизм фокуса, обработку событий клавиатуры и интеграцию с интерактивными слоями карты.
Базовая модель взаимодействия строится вокруг перехвата стандартных клавиш (стрелки, Tab, +/-) и преобразования их в действия камеры: панорамирование, масштабирование и вращение.
По умолчанию карта поддерживает следующие действия:
+ / - — увеличение и уменьшение
масштабаShift + стрелки — более быстрый сдвигShift + Alt + стрелки — вращение и наклон (если
включено pitch/rotation)Фокус на карте устанавливается через контейнер
map.getCanvasContainer() и внутренние обработчики
событий.
map.keyboard.enable();
Клавиатурное управление активируется автоматически при инициализации карты, но может быть отключено.
Основной механизм управления реализован через класс
KeyboardHandler. Он может быть явно добавлен или
настроен:
const keyboard = new mapboxgl.KeyboardHandler();
map.addControl({
onAdd: function(map) {
this.map = map;
return document.createElement('div');
},
onRemove: function() {}
});
Однако в стандартной архитектуре используется встроенный контрол:
map.keyboard.enable();
map.keyboard.disable();
Клавиатурное управление можно динамически включать и отключать в зависимости от сценария приложения.
map.keyboard.disable();
Используется при:
map.keyboard.enable();
Карта становится доступной для клавиатуры только при наличии фокуса.
В DOM-структуре Mapbox GL JS canvas получает tabindex,
позволяющий включать его в порядок навигации.
map.getCanvas().setAttribute('tabindex', '0');
map.getCanvas().focus();
Без этого шага клавиши не будут обрабатываться.
Часто применяются стратегии:
map.on('load', () => {
map.getCanvas().focus();
});
Низкоуровневое управление возможно через DOM-события:
map.getCanvas().addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
map.panBy([0, -50]);
}
});
Этот подход используется для полного переопределения стандартного поведения.
Важно учитывать:
e.preventDefault() при
кастомной логикеКлючевые методы управления камерой:
map.panBy([dx, dy], { duration: 200 });
map.zoomIn();
map.zoomOut();
Привязка к клавишам:
map.getCanvas().addEventListener('keydown', (e) => {
switch (e.key) {
case '+':
map.zoomIn();
break;
case '-':
map.zoomOut();
break;
}
});
При включённых параметрах pitch и bearing
доступно управление вращением:
map.rotateTo(map.getBearing() + 15);
map.easeTo({ pitch: map.getPitch() + 10 });
Комбинации клавиш часто используют модификаторы:
map.getCanvas().addEventListener('keydown', (e) => {
if (e.shiftKey && e.key === 'ArrowLeft') {
map.rotateTo(map.getBearing() - 10);
}
});
Клавиатурная навигация должна учитывать взаимодействие с объектами карты:
const markerElement = marker.getElement();
markerElement.setAttribute('tabindex', '0');
markerElement.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
popup.addTo(map);
}
});
Popups должны быть закрываемыми через клавиатуру:
popup.on('open', () => {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
popup.remove();
}
});
});
Ключевой момент — корректное управление фокусом после закрытия:
popup.on('close', () => {
map.getCanvas().focus();
});
Карта должна корректно вписываться в общий порядок tab-index на странице.
Рекомендуемая структура:
tabindex="0" для контейнера картыdocument.querySelector('#map').setAttribute('tabindex', '0');
В сложных приложениях стандартные бинды заменяются:
const keyMap = {
ArrowUp: () => map.panBy([0, -100]),
ArrowDown: () => map.panBy([0, 100]),
ArrowLeft: () => map.panBy([-100, 0]),
ArrowRight: () => map.panBy([100, 0])
};
map.getCanvas().addEventListener('keydown', (e) => {
if (keyMap[e.key]) {
keyMap[e.key]();
}
});
Такой подход используется в:
Некоторые клавиши перехватываются браузером:
Для корректной работы требуется:
e.preventDefault();
e.stopPropagation();
Клавиатурная навигация должна сопровождаться корректной семантикой:
role="application" для контейнера картыaria-labelmap.getCanvas().setAttribute('role', 'application');
map.getCanvas().setAttribute('aria-label', 'Интерактивная карта');
Если карта находится рядом с input/textarea, требуется временное отключение управления:
input.addEventListener('focus', () => map.keyboard.disable());
input.addEventListener('blur', () => map.keyboard.enable());
Это предотвращает перехват стрелок и других клавиш.
Частые события keydown могут вызывать лишние пересчёты
камеры. Оптимизация включает:
panBy,
easeTomap.keyboard.disable()Клавиатурные действия часто синхронизируются с анимацией:
map.easeTo({
center: map.getCenter(),
zoom: map.getZoom() + 1,
duration: 300
});
Это обеспечивает плавный переход между состояниями вместо резких скачков.
Клавиатурная навигация взаимодействует с:
и может быть перекрыта их внутренними обработчиками при активном фокусе.