Интерфейсный компонент «аккордеон» представляет собой набор связанных секций, каждая из которых содержит заголовок и скрываемую область содержимого. При взаимодействии с заголовком соответствующий блок раскрывается или сворачивается. В отличие от классического аккордеона, где допускается открытие только одной секции одновременно, аккордеон с множественным раскрытием допускает одновременное отображение нескольких панелей.
Такой подход применяется в ситуациях, когда пользователю требуется сравнивать содержимое нескольких секций или держать их открытыми параллельно. Примеры использования:
С точки зрения доступности подобные интерфейсы требуют корректной реализации клавиатурной навигации, управления фокусом и правильной семантики. Библиотека ally.js предоставляет набор инструментов для решения этих задач.
При реализации доступного аккордеона необходимо учитывать несколько важных аспектов.
Каждая секция состоит из двух элементов:
Обычно используется следующая схема:
button → управляет panel
Кнопка должна иметь связь с панелью через атрибуты:
aria-controlsaria-expandedПанель содержимого содержит:
id, на который ссылается кнопкаrole="region" или аналогичную семантикуСтандартная доступная модель предполагает поддержку:
Библиотека ally.js упрощает реализацию корректной обработки фокуса и управления интерактивностью.
Библиотека ally.js предназначена для управления доступностью интерфейсов. В аккордеонах она применяется для решения следующих задач:
Особенно важно корректно управлять фокусом, когда панели скрываются или отображаются.
Если панель скрыта через CSS (display: none или
hidden), вложенные элементы не должны оставаться доступными
для клавиатурной навигации. ally.js позволяет динамически изменять их
состояние.
Аккордеон с множественным раскрытием может иметь следующую разметку:
<div class="accordion">
<h3>
<button
class="accordion-toggle"
aria-expanded="false"
aria-controls="panel-1"
id="accordion-1">
Раздел 1
</button>
</h3>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-1"
hidden>
<p>Содержимое первой панели.</p>
</div>
<h3>
<button
class="accordion-toggle"
aria-expanded="false"
aria-controls="panel-2"
id="accordion-2">
Раздел 2
</button>
</h3>
<div
id="panel-2"
role="region"
aria-labelledby="accordion-2"
hidden>
<p>Содержимое второй панели.</p>
</div>
</div>
Ключевые элементы:
hidden;aria-controls и
aria-labelledby.Библиотека может подключаться через пакетный менеджер или CDN.
Пример подключения:
<script src="https://unpkg.com/ally.js/ally.min.js"></script>
После загрузки становятся доступны инструменты управления доступностью.
Аккордеон с множественным раскрытием не требует закрытия других секций. Каждая кнопка управляет только своей панелью.
Базовая логика переключения:
const toggles = document.querySelectorAll('.accordion-toggle');
toggles.forEach(button => {
button.addEventListener('click', () => {
const expanded = button.getAttribute('aria-expanded') === 'true';
const panel = document.getElementById(button.getAttribute('aria-controls'));
button.setAttribute('aria-expanded', !expanded);
if (expanded) {
panel.hidden = true;
} else {
panel.hidden = false;
}
});
});
Каждая секция функционирует независимо.
При скрытии панели важно исключить вложенные элементы из навигации. ally.js предоставляет модуль maintain.disabled, который позволяет временно отключать интерактивность элементов.
Пример:
import maintainDisabled from 'ally.js/maintain/disabled';
После закрытия панели можно отключить доступ к вложенным элементам.
maintainDisabled({
context: panel
});
При повторном открытии панели ограничения снимаются.
Это предотвращает попадание фокуса на элементы внутри скрытого содержимого.
Библиотека содержит утилиту:
ally.query.focusable
Она позволяет получить список элементов, доступных для фокуса внутри панели.
Пример:
import queryFocusable from 'ally.js/query/focusable';
const elements = queryFocusable({
context: panel
});
Полученный список можно использовать для:
Для аккордеонов часто реализуется перемещение между заголовками при помощи стрелок.
Пример обработки:
const buttons = Array.from(document.querySelectorAll('.accordion-toggle'));
buttons.forEach((button, index) => {
button.addEventListener('keydown', event => {
if (event.key === 'ArrowDown') {
event.preventDefault();
const next = buttons[index + 1] || buttons[0];
next.focus();
}
if (event.key === 'ArrowUp') {
event.preventDefault();
const prev = buttons[index - 1] || buttons[buttons.length - 1];
prev.focus();
}
});
});
Фокус циклически перемещается между кнопками.
В ally.js присутствует инструмент:
ally.element.disabled
Он позволяет программно включать или отключать интерактивность элемента.
Пример:
import elementDisabled from 'ally.js/element/disabled';
elementDisabled(panel, true);
Это делает панель недоступной для взаимодействия.
При открытии:
elementDisabled(panel, false);
Таким образом обеспечивается корректная работа интерфейса для клавиатурной навигации и вспомогательных технологий.
При открытии панели часто возникает необходимость перенести фокус к первому интерактивному элементу внутри.
В ally.js используется утилита:
ally.query.firstTabbable
Пример:
import firstTabbable from 'ally.js/query/first-tabbable';
const element = firstTabbable({
context: panel
});
if (element) {
element.focus();
}
Это особенно полезно в длинных панелях с формами или интерактивными компонентами.
При использовании CSS-анимаций необходимо учитывать, что:
Распространённый подход:
ally.js облегчает управление этими состояниями.
При наличии десятков секций важно учитывать производительность и управление состоянием.
Оптимизации:
Пример делегирования:
document.querySelector('.accordion')
.addEventListener('click', event => {
const button = event.target.closest('.accordion-toggle');
if (!button) return;
const panel = document.getElementById(button.getAttribute('aria-controls'));
const expanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', !expanded);
panel.hidden = expanded;
});
Это уменьшает количество обработчиков.
После реализации аккордеона рекомендуется проверить:
aria-expanded;aria-controls;ally.js можно использовать для диагностики:
Экранные дикторы воспринимают кнопку аккордеона следующим образом:
Атрибут aria-expanded обеспечивает передачу этой
информации.
Панель с role="region" и aria-labelledby
объявляется как область, связанная с заголовком.
Такая структура позволяет пользователю вспомогательных технологий понимать структуру аккордеона и управлять его секциями независимо.
Распространённые проблемы:
Использование ссылок вместо кнопок
<a href="#">Раздел</a>
Это нарушает семантику. Управляющий элемент должен быть
button.
Отсутствие aria-expanded
Без этого атрибута экранный диктор не сообщает состояние панели.
Фокус внутри скрытых панелей
Если элементы остаются доступными для Tab-навигации, пользователь может попасть в невидимую область.
Неправильная обработка клавиатуры
Некоторые реализации поддерживают только мышь, игнорируя клавиатуру.
В крупных интерфейсах аккордеон реализуется как модуль:
Структура может включать:
Accordion
├─ AccordionItem
│ ├─ ToggleButton
│ └─ Panel
└─ AccessibilityController
Контроллер доступности взаимодействует с ally.js и управляет:
Аккордеоны с множественным раскрытием часто используются в:
При масштабировании важно учитывать:
Библиотека ally.js обеспечивает низкоуровневые инструменты для реализации этих требований, позволяя создавать гибкие и доступные интерфейсы с большим количеством независимых секций.