Управление backdrop

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

Основные задачи backdrop

  • Блокировка фонового контента: при открытом модальном окне пользователь не должен иметь возможность случайно взаимодействовать с элементами страницы за ним.
  • Контроль фокуса: фокус клавиатуры и скринридеров должен оставаться внутри модального окна, предотвращая уход на скрытые элементы.
  • Доступность для вспомогательных технологий: корректное объявление ролей и атрибутов ARIA позволяет скринридерам корректно воспринимать контент.

Инициализация backdrop через ally.js

Для управления backdrop используется метод trapFocus. Он позволяет задать область, в которой будет ограничен фокус пользователя. Пример базовой инициализации:

import * as ally from 'ally.js';

const modal = document.getElementById('modal');
const backdrop = document.getElementById('backdrop');

function openModal() {
  backdrop.style.display = 'block';
  
  ally.trapFocus({
    context: modal,
    ignoreAccessiblyHidden: true,
  });
}

Ключевые параметры trapFocus:

  • context — элемент, внутри которого фокус будет ограничен.
  • ignoreAccessiblyHidden — игнорировать элементы с aria-hidden="true".
  • defaultElement — элемент, который получает фокус по умолчанию при активации trap.
  • clickOutsideDeactivates — позволяет закрывать модальное окно при клике вне него.

Работа с закрытием backdrop

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

function closeModal() {
  backdrop.style.display = 'none';
  
  if (trap) {
    trap.deactivate();
  }
}

Здесь trap — объект, возвращаемый функцией ally.trapFocus. Его метод deactivate() снимает ограничения на фокус и возвращает пользователю контроль над остальными элементами страницы.

Обработка кликов и клавиатуры

Для повышения удобства и доступности рекомендуется:

  • Закрывать модальное окно по клавише Escape.
  • Предотвращать взаимодействие с задним планом при активном backdrop.
  • Сохранять фокус на первом доступном интерактивном элементе внутри модального окна.

Пример:

modal.addEventListener('keydown', (event) => {
  if (event.key === 'Escape') {
    closeModal();
  }
});

backdrop.addEventListener('click', () => {
  closeModal();
});

Управление несколькими модальными окнами

ally.js позволяет организовывать stacking модальных окон, сохраняя порядок фокусировки:

const firstTrap = ally.trapFocus({ context: firstModal });
const secondTrap = ally.trapFocus({ context: secondModal });

secondTrap.activate(); // фокус ограничен вторым модальным
firstTrap.deactivate(); // предыдущий trap временно отключен

Важно при этом корректно возвращать активность предыдущему trap при закрытии верхнего модального окна.

Дополнительные функции для backdrop

  • Inert управление: ally.js позволяет временно отключать взаимодействие с контентом вне модального окна с помощью inert.
  • События активации/деактивации: можно подписываться на события trapFocus:activate и trapFocus:deactivate для кастомной логики (например, анимаций или уведомлений скринридера).
modal.addEventListener('trapFocus:activate', () => {
  console.log('Фокус захвачен модальным окном');
});

modal.addEventListener('trapFocus:deactivate', () => {
  console.log('Фокус возвращён к основной странице');
});

Рекомендации по доступности

  • Устанавливать aria-hidden="true" для фонового контента при активном backdrop.
  • Использовать role="dialog" и aria-modal="true" для модального окна.
  • Гарантировать, что первый интерактивный элемент внутри модального окна получает фокус при открытии.
  • Проверять корректную работу с клавиатурной навигацией и скринридерами.

Практический пример полного сценария

const modal = document.getElementById('modal');
const backdrop = document.getElementById('backdrop');
let trap;

function openModal() {
  backdrop.style.display = 'block';
  modal.setAttribute('aria-hidden', 'false');

  trap = ally.trapFocus({
    context: modal,
    defaultElement: modal.querySelector('button, a, input'),
    clickOutsideDeactivates: true
  });
}

function closeModal() {
  backdrop.style.display = 'none';
  modal.setAttribute('aria-hidden', 'true');

  if (trap) {
    trap.deactivate();
  }
}

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