Фокус и табуляция

В интерфейсах, построенных на HERE Maps API for JavaScript, управление фокусом определяет, как карта и её элементы реагируют на клавиатурную навигацию, переходы между элементами страницы и взаимодействие с доступными компонентами UI. Корректная настройка фокуса критична для доступности, предсказуемого поведения интерфейса и интеграции карты в сложные веб-приложения.

Карта по умолчанию представляет собой интерактивный контейнер, который может перехватывать события клавиатуры, но не всегда участвует в стандартной табуляции документа. Поведение фокуса зависит от конфигурации контейнера, UI-компонентов и подключённых обработчиков событий.


Контейнер карты и участие в табуляции

HTML-элемент, в который монтируется карта, не обязан участвовать в последовательности табуляции. Для управления этим поведением используется атрибут tabindex.

<div id="map" tabindex="0"></div>

Значение tabindex="0" добавляет контейнер карты в естественный порядок табуляции документа. При этом карта становится фокусируемой и может принимать клавиатурные события.

Альтернативный вариант:

<div id="map" tabindex="-1"></div>

Такой контейнер исключается из табуляции, но может быть активирован программно:

document.getElementById('map').focus();

Подход с tabindex="-1" часто используется при необходимости управлять фокусом через UI-кнопки или сценарии навигации.


Роль карты в доступности и ARIA-атрибуты

Для корректной интерпретации карты вспомогательными технологиями используется назначение роли и описательных атрибутов:

<div id="map"
     role="application"
     aria-label="Интерактивная карта">
</div>

Роль application указывает, что внутри контейнера действует собственная модель обработки клавиатуры, отличная от стандартной веб-страницы. Это означает, что клавиши могут интерпретироваться как команды управления картой, а не как навигация по DOM.

Альтернативный подход — использование role="region" для более мягкой интеграции в страницу, когда карта не должна полностью перехватывать управление клавиатурой.


Перехват клавиатурных событий

HERE Maps API позволяет обрабатывать события клавиатуры через стандартные DOM-обработчики:

const mapElement = document.getElementById('map');

mapElement.addEventListener('keydown', (event) => {
    switch (event.key) {
        case 'ArrowUp':
            map.setCenter(map.getCenter().translate(0, 0.001));
            break;
        case 'ArrowDown':
            map.setCenter(map.getCenter().translate(0, -0.001));
            break;
        case '+':
            map.setZoom(map.getZoom() + 1);
            break;
        case '-':
            map.setZoom(map.getZoom() - 1);
            break;
    }
});

Внутренние механизмы карты также могут обрабатывать клавиши навигации, поэтому важно учитывать возможные конфликты между пользовательской логикой и встроенными обработчиками.


Фокус внутри UI-компонентов карты

UI-слой HERE Maps (H.ui.UI) добавляет интерактивные элементы: кнопки масштабирования, переключатели слоёв, панели управления. Эти элементы участвуют в табуляции автоматически.

Последовательность перехода фокуса обычно включает:

  • элементы управления масштабом
  • переключатели интерфейса
  • пользовательские контролы
  • возврат к документу

Контейнеры UI используют стандартные HTML-кнопки, что обеспечивает встроенную поддержку клавиатуры без дополнительной логики.


Управление табуляцией внутри карты

При сложных интерфейсах может возникать необходимость контролировать порядок перехода между элементами карты и внешними компонентами страницы. Для этого используется:

  • управление tabindex
  • программный вызов focus()
  • блокировка стандартного поведения клавиш Tab
  • реализация «roving tabindex»

Пример ограничения перехода:

mapElement.addEventListener('keydown', (event) => {
    if (event.key === 'Tab') {
        event.preventDefault();
        // кастомная логика переключения фокуса
    }
});

Такой подход применяется в случаях, когда карта встроена в сложный интерфейс с собственным управлением фокусом.


Режим application и особенности поведения клавиш

При использовании role="application" клавиши стрелок и другие управляющие клавиши перестают выполнять стандартную прокрутку страницы. Вместо этого они могут быть интерпретированы как команды управления картой.

Типичное поведение:

  • стрелки — перемещение карты
  • PageUp/PageDown — изменение масштаба или перемещение
  • Enter — активация выбранных объектов
  • Esc — выход из активного режима взаимодействия

При этом важно учитывать необходимость возврата фокуса в документ, чтобы избежать «залипания» клавиатурного управления внутри карты.


Фокус на маркерах и объектах карты

Маркерные объекты (H.map.Marker) сами по себе не являются DOM-элементами, но могут быть связаны с интерактивными событиями.

Для обеспечения доступности и фокусируемости используется:

  • обработка событий tap и click
  • привязка клавиатурных событий через контейнер
  • отображение информационных окон

Пример:

marker.addEventListener('tap', () => {
    infoBubble.setPosition(marker.getGeometry());
    infoBubble.open();
});

При необходимости можно эмулировать фокус на объекте:

mapElement.focus();
selectedMarker = marker;

Информационные окна и управление фокусом

Информационные окна (H.ui.InfoBubble) часто требуют отдельного контроля фокуса, особенно при открытии поверх карты.

Типичный сценарий:

  1. открытие окна по событию маркера
  2. перенос фокуса на контейнер окна
  3. блокировка взаимодействия с картой до закрытия
infoBubble.addEventListener('open', () => {
    infoBubble.getContentElement().focus();
});

При закрытии окна фокус возвращается к маркеру или карте:

infoBubble.addEventListener('close', () => {
    mapElement.focus();
});

Проблемы «утечки» фокуса

Одной из типичных проблем является потеря фокуса после взаимодействия с UI-элементами карты. Это проявляется в том, что Tab-навиация выходит за пределы логической структуры интерфейса.

Причины:

  • отсутствие корректного tabindex
  • динамическое добавление DOM-элементов без обновления порядка фокуса
  • конфликт пользовательских обработчиков клавиш
  • перекрытие модальными окнами без управления focus trap

Решение заключается в явном контроле входа и выхода фокуса из компонентов карты.


Захват фокуса (focus trap) в модальных сценариях

При отображении модальных панелей поверх карты требуется ограничить навигацию внутри активного слоя:

function trapFocus(container) {
    const focusable = container.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );

    focusable[0].focus();

    container.addEventListener('keydown', (e) => {
        if (e.key === 'Tab') {
            const first = focusable[0];
            const last = focusable[focusable.length - 1];

            if (e.shiftKey && document.activeElement === first) {
                e.preventDefault();
                last.focus();
            } else if (!e.shiftKey && document.activeElement === last) {
                e.preventDefault();
                first.focus();
            }
        }
    });
}

Такой механизм предотвращает уход фокуса на карту или другие элементы страницы.


Взаимодействие фокуса и масштабирования

Клавиатурное управление масштабом тесно связано с состоянием фокуса карты. При потере фокуса обработка клавиш + и - прекращается.

Типичное поведение:

  • карта в фокусе — активное масштабирование клавишами
  • карта не в фокусе — клавиши обрабатываются документом или браузером

Это требует явного управления фокусом при переключении режимов интерфейса.


Блокировка скролла и конфликтов с прокруткой страницы

При активной карте часто возникает конфликт между прокруткой страницы и перемещением карты. Решение заключается в подавлении стандартного поведения колесика мыши и клавиш прокрутки при фокусе карты.

mapElement.addEventListener('wheel', (event) => {
    event.preventDefault();
    map.setZoom(map.getZoom() + (event.deltaY < 0 ? 1 : -1));
});

При этом важно не блокировать скролл страницы вне активного состояния карты, чтобы не нарушать общую навигацию интерфейса.


Связь табуляции с динамическими слоями карты

При добавлении слоёв (H.map.layer.TileLayer, пользовательские overlays) порядок фокуса не меняется автоматически, поскольку эти элементы не входят в DOM.

Однако связанные UI-компоненты могут динамически появляться и исчезать, что требует:

  • пересчёта порядка tabindex
  • обновления focusable-элементов
  • сброса активного фокуса при удалении слоя

Поведение фокуса при смене состояния карты

При переключении режимов (например, 2D/3D, ночная тема, изменение слоёв) рекомендуется сохранять контекст фокуса:

  • если фокус был на карте — он остаётся на карте
  • если фокус был внутри UI — он восстанавливается после обновления интерфейса
const active = document.activeElement;

map.setBaseLayer(newLayer);

setTimeout(() => {
    active.focus();
}, 0);

Такой подход предотвращает неожиданный сброс пользовательского контекста взаимодействия.