Focus management

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

Инициализация и базовая настройка

Для начала Headroom подключается к DOM-элементу шапки следующим образом:

var header = document.querySelector("header");
var headroom = new Headroom(header, {
    tolerance: {
        up: 5,
        down: 5
    },
    offset: 50,
    classes: {
        initial: "headroom",
        pinned: "headroom--pinned",
        unpinned: "headroom--unpinned"
    }
});
headroom.init();

Ключевые моменты для фокус-менеджмента:

  • pinned — класс, который добавляется, когда шапка видима.
  • unpinned — класс, когда шапка скрыта.
  • initial — исходное состояние до применения анимации.

Фокус-менеджмент часто зависит от этих классов, так как скрытая шапка может содержать элементы с интерактивными компонентами, такими как кнопки и ссылки. Необходимо гарантировать, что скрытые элементы не доступны через клавиатуру, чтобы избежать нарушения UX.

Управление видимостью элементов для клавиатуры

При добавлении Headroom.js важно синхронизировать ARIA-атрибуты и tabindex с состояниями шапки:

header.addEventListener("headroom:unpinned", function() {
    const focusableElements = header.querySelectorAll("a, button, input, select, textarea");
    focusableElements.forEach(el => {
        el.setAttribute("tabindex", "-1");
    });
});

header.addEventListener("headroom:pinned", function() {
    const focusableElements = header.querySelectorAll("a, button, input, select, textarea");
    focusableElements.forEach(el => {
        el.removeAttribute("tabindex");
    });
});

Пояснения:

  • tabindex="-1" исключает элемент из последовательности табуляции.
  • Удаление tabindex возвращает стандартное поведение.
  • Использование событий headroom:unpinned и headroom:pinned обеспечивает синхронизацию с анимацией шапки.

Поддержка доступности (ARIA)

Для повышения доступности элементов шапки при изменении её состояния рекомендуется использовать ARIA-атрибуты:

header.setAttribute("aria-hidden", "false");

header.addEventListener("headroom:unpinned", function() {
    header.setAttribute("aria-hidden", "true");
});

header.addEventListener("headroom:pinned", function() {
    header.setAttribute("aria-hidden", "false");
});

Принципы:

  • aria-hidden="true" скрывает элемент от экранных читалок.
  • aria-hidden="false" делает его вновь доступным.
  • Совместное использование с tabindex предотвращает попадание фокуса на скрытые элементы.

Автоматическая фокусировка при показе шапки

В некоторых интерфейсах важно, чтобы при возвращении шапки пользовательский фокус автоматически устанавливался на ключевой элемент (например, на кнопку меню или логотип):

header.addEventListener("headroom:pinned", function() {
    const mainButton = header.querySelector("button.menu-toggle");
    if(mainButton) mainButton.focus();
});

Рекомендации:

  • Фокусировка должна происходить только после завершения анимации появления (pinned).
  • Избегать перемещения фокуса на скрытые элементы, так как это нарушает последовательность табуляции.

Обработка динамических элементов

Если содержимое шапки динамически изменяется (например, добавляются ссылки при авторизации пользователя), необходимо обновлять фокус-менеджмент после каждой модификации:

function updateFocusManagement() {
    const focusableElements = header.querySelectorAll("a, button, input, select, textarea");
    focusableElements.forEach(el => {
        if(header.classList.contains("headroom--unpinned")) {
            el.setAttribute("tabindex", "-1");
        } else {
            el.removeAttribute("tabindex");
        }
    });
}

// Вызов после изменения DOM
updateFocusManagement();

Это гарантирует, что новые элементы автоматически учитываются при управлении фокусом.

Совмещение с другими библиотеками управления фокусом

При интеграции с модальными окнами, сайдбарами или библиотеками типа FocusTrap необходимо учитывать, что Headroom.js может скрывать элементы, на которые установлены ограничения фокуса. Рекомендуется:

  • Перед скрытием шапки проверять, находится ли фокус внутри неё.
  • При необходимости перемещать фокус на ближайший видимый интерактивный элемент вне шапки.
  • Сохранять совместимость с aria-hidden и tabindex для поддержания последовательности навигации.

Практические советы

  • Сочетать события Headroom с утилитами по управлению фокусом (focusin, focusout), чтобы избежать потери фокуса при динамическом скрытии шапки.
  • Использовать CSS-переходы совместно с JS, чтобы синхронизация классов и атрибутов происходила после завершения анимации.
  • Для сложных интерфейсов создавать отдельный модуль управления фокусом, который подписан на события headroom:pinned и headroom:unpinned, чтобы централизованно управлять доступностью элементов.

Этот подход обеспечивает полную совместимость Headroom.js с требованиями доступности, предотвращает случайное попадание фокуса на скрытые элементы и позволяет интегрировать шапку в сложные интерактивные интерфейсы.