Вопросы доступности в веб-разработке
Обеспечение доступности веб-сайтов важно для создания инклюзивного цифрового опыта. Узнайте о методах, ARIA и управлении фокусом.
Обеспечение веб-доступности необходимо для создания инклюзивного цифрового опыта. Доступность не только помогает пользователям с ограниченными возможностями, но и улучшает общий пользовательский опыт и расширяет вашу аудиторию. В этом руководстве рассматриваются важность доступности, методы создания доступных манипуляций с DOM, роль ARIA (Accessible Rich Internet Applications) и способы обеспечения удобства использования динамических JavaScript-виджетов для всех пользователей.
Когда вы реализуете интерактивность с помощью JavaScript — переключаете контент, выбираете и обновляете элементы или изменяете документ — вы берёте на себя ответственность за доступность, которую браузер иначе обеспечивал бы автоматически с помощью обычного HTML. В этой главе объясняется, в чём заключается эта ответственность и как её выполнить.
Создание доступного контента
Важность доступности в веб-разработке
Доступность в веб-разработке гарантирует, что все пользователи, включая людей с ограниченными возможностями, могут эффективно получать доступ к веб-контенту и взаимодействовать с ним. По оценкам Всемирной организации здравоохранения, более 1 миллиарда человек живут с той или иной формой инвалидности. Делая веб-контент доступным, вы охватываете более широкую аудиторию, повышаете удобство использования и соответствуете правовым стандартам, таким как Американский закон о гражданах с ограниченными возможностями (ADA) и Руководство по обеспечению доступности веб-контента (WCAG).
Преимущества доступности
- Инклюзивность: позволяет пользователям с различными ограниченными возможностями получать доступ к информации и сервисам.
- Улучшение SEO: поисковые системы нередко повышают рейтинг доступных сайтов.
- Соответствие правовым нормам: помогает избежать потенциальных правовых проблем, связанных со стандартами доступности.
- Повышение удобства использования: улучшает общий пользовательский опыт для всех посетителей, включая людей без ограниченных возможностей.
Методы создания доступных манипуляций с DOM
Навигация с клавиатуры
Многие пользователи не могут использовать мышь — они перемещаются с помощью клавиатуры, переключателя или программы чтения с экрана, управляемой клавиатурой. Если элемент управления реагирует только на клики, такие пользователи лишаются доступа. Правило простое: всё, что может сделать пользователь мыши, должен уметь делать и пользователь клавиатуры.
Убедитесь, что все интерактивные элементы доступны через клавиатуру. Для навигации по Tab используйте естественный порядок DOM, чтобы сохранить логический поток. Атрибут tabindex управляет этим:
tabindex="0"включает элемент в естественный порядок перехода по Tab (полезно, когда неродной элемент делается интерактивным).tabindex="-1"исключает его из порядка Tab, но позволяет фокусировать его программно с помощьюelement.focus().- Положительные значения, например
tabindex="3", переопределяют естественный порядок и почти всегда вызывают путаницу — их следует избегать.
Родные элементы, такие как <button>, <a href> и элементы форм, по умолчанию доступны через клавиатуру — это одна из главных причин предпочитать их кликабельным <div>. Всегда убедитесь, что видимый индикатор фокуса (обводка) стилизован так, чтобы пользователи клавиатуры чётко видели, на каком элементе находится фокус — никогда не задавайте outline: none без замены.
<!DOCTYPE html>
<html>
<head>
<title>Keyboard Navigation Example</title>
</head>
<body>
<h4>Press the 'Tab' key to navigate through the buttons!</h4>
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
</body>
</html>Этот пример опирается на естественный порядок DOM для кнопок, что упрощает навигацию для пользователей клавиатуры.
Доступные интерактивные компоненты
Этот пример демонстрирует создание доступного аккордеона с использованием ролей и свойств ARIA, а также эффективное управление фокусом.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Accessible Accordion Example</title>
<style>
.accordion {
border: 1px solid #ccc;
border-radius: 5px;
margin: 20px 0;
}
.accordion-header {
padding: 10px;
cursor: pointer;
background-color: #f0f0f0;
border-bottom: 1px solid #ccc;
width: 100%;
text-align: left;
}
.accordion-content {
display: none;
padding: 10px;
}
</style>
</head>
<body>
<h1>Accessible Accordion Example</h1>
<h4>Use your keyboard (Enter or Space key) to toggle the accordion!</h4>
<div class="accordion">
<button class="accordion-header" id="accordion-header-1" aria-controls="accordion-content-1" aria-expanded="false">
Section 1
</button>
<div class="accordion-content" id="accordion-content-1" role="region" aria-labelledby="accordion-header-1" tabindex="-1">
<p>This is the content of section 1.</p>
</div>
</div>
<div class="accordion">
<button class="accordion-header" id="accordion-header-2" aria-controls="accordion-content-2" aria-expanded="false">
Section 2
</button>
<div class="accordion-content" id="accordion-content-2" role="region" aria-labelledby="accordion-header-2" tabindex="-1">
<p>This is the content of section 2.</p>
</div>
</div>
<div class="accordion">
<button class="accordion-header" id="accordion-header-3" aria-controls="accordion-content-3" aria-expanded="false">
Section 3
</button>
<div class="accordion-content" id="accordion-content-3" role="region" aria-labelledby="accordion-header-3" tabindex="-1">
<p>This is the content of section 3.</p>
</div>
</div>
<script>
document.querySelectorAll('.accordion-header').forEach(header => {
header.addEventListener('click', function () {
const expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !expanded);
const content = document.getElementById(this.getAttribute('aria-controls'));
content.style.display = !expanded ? 'block' : 'none';
});
header.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.click();
}
});
});
</script>
</body>
</html>- Структура аккордеона: аккордеон состоит из заголовков, при нажатии на которые разворачивается или сворачивается связанный контент.
- Семантический HTML и ARIA:
- Родные элементы
<button>используются для заголовков, обеспечивая встроенную поддержку клавиатуры и программ чтения с экрана. aria-controlsсвязывает заголовки с их контентом.aria-expandedпоказывает состояние секции аккордеона.role="region"на секциях контента идентифицирует их как значимые области.
- Родные элементы
- Доступность с клавиатуры:
- Обработчики событий реагируют на события
clickиkeydown, позволяя переключать аккордеон с клавиатуры (Enter или Space).
- Обработчики событий реагируют на события
Почему это важно:
- Улучшенное удобство использования: аккордеон доступен с помощью мыши и клавиатуры.
- Повышенная доступность: атрибуты ARIA передают состояние и структуру вспомогательным технологиям, делая его доступным для пользователей программ чтения с экрана.
- Управление фокусом: фокус остаётся на кнопке-триггере в соответствии со стандартными паттернами аккордеона, предотвращая непредвиденные переходы навигации для пользователей клавиатуры.
Подробнее о настройке обработчиков событий клавиатуры и кликов см. в разделе Обработка событий в DOM.
Семантический HTML
Используйте семантические HTML-элементы для передачи смысла и структуры контента. Это помогает вспомогательным технологиям эффективно интерпретировать веб-контент и перемещаться по нему.
<!DOCTYPE html>
<html>
<head>
<title>Semantic HTML Example</title>
</head>
<body>
<header>
<h1>Main Heading</h1>
</header>
<nav>
<ul>
<li><a href="#section1">Section 1</a></li>
<li><a href="#section2">Section 2</a></li>
</ul>
</nav>
<main>
<section id="section1">
<h2>Section 1</h2>
<p>Content for section 1.</p>
</section>
<section id="section2">
<h2>Section 2</h2>
<p>Content for section 2.</p>
</section>
</main>
<footer>
<p>Footer content</p>
</footer>
</body>
</html>Этот пример использует семантические HTML-элементы, такие как <header>, <nav>, <main>, <section> и <footer>, для определения структуры страницы.
Текстовые альтернативы
Программы чтения с экрана не могут интерпретировать изображения, иконки или рисунки на canvas — они читают текстовую альтернативу, которую вы предоставляете. Каждое значимое изображение должно иметь атрибут alt, описывающий его содержимое или назначение. Декоративные изображения, не несущие информации, должны иметь пустой alt="", чтобы программа чтения с экрана пропускала их, а не объявляла имя файла.
<!-- Meaningful image: describe it -->
<img src="chart.png" alt="Sales rose 40% from January to March" />
<!-- Decorative image: hide it from screen readers -->
<img src="divider.png" alt="" />
<!-- Icon-only button: label it -->
<button aria-label="Close dialog">×</button>Когда вы динамически создаёте изображения или кнопки с иконками с помощью JavaScript, устанавливайте alt или aria-label одновременно с созданием элемента — никогда не публикуйте элемент управления без метки.
Управление фокусом в динамическом контенте
Когда JavaScript открывает диалоговое окно, показывает новый контент или проводит пользователя через многоэтапный процесс, необходимо целенаправленно перемещать фокус. Иначе пользователь клавиатуры или программы чтения с экрана остаётся на прежней позиции и не знает, что что-то изменилось. Пример ниже открывает модальное окно, перемещает в него фокус, удерживает его там, чтобы Tab не мог выйти за пределы, и возвращает фокус на кнопку-триггер при закрытии.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Accessible Modal Example</title>
<style>
.overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.5); }
.overlay.open { display: flex; align-items: center; justify-content: center; }
.dialog { background: #fff; padding: 20px; border-radius: 6px; min-width: 260px; }
</style>
</head>
<body>
<button id="open-btn">Open dialog</button>
<div class="overlay" id="overlay">
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm action</h2>
<p>Are you sure you want to continue?</p>
<button id="confirm-btn">Confirm</button>
<button id="close-btn">Cancel</button>
</div>
</div>
<script>
const overlay = document.getElementById('overlay');
const openBtn = document.getElementById('open-btn');
const closeBtn = document.getElementById('close-btn');
let lastFocused = null;
function openDialog() {
lastFocused = document.activeElement; // remember the trigger
overlay.classList.add('open');
document.getElementById('confirm-btn').focus(); // move focus in
}
function closeDialog() {
overlay.classList.remove('open');
if (lastFocused) lastFocused.focus(); // restore focus
}
openBtn.addEventListener('click', openDialog);
closeBtn.addEventListener('click', closeDialog);
// Trap focus inside the dialog and close on Escape
overlay.addEventListener('keydown', function (event) {
if (event.key === 'Escape') { closeDialog(); return; }
if (event.key !== 'Tab') return;
const focusable = overlay.querySelectorAll('button');
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
});
</script>
</body>
</html>Ключевые моменты: role="dialog" и aria-modal="true" сообщают вспомогательным технологиям, что это модальное окно; aria-labelledby задаёт ему доступное имя; фокус перемещается внутрь при открытии и возвращается на триггер при закрытии; Tab/Shift+Tab циклически переключаются внутри диалога, не выходя за его пределы. Подробнее о программном управлении фокусом см. в разделе Фокусировка: focus / blur.
ARIA (Accessible Rich Internet Applications)
Подробнее об ARIA
Как вы уже знаете, ARIA (Accessible Rich Internet Applications) — это набор атрибутов, которые можно добавить к HTML-элементам для улучшения доступности для пользователей вспомогательных технологий, например программ чтения с экрана. Атрибуты ARIA помогают определять роли, свойства и состояния элементов, делая веб-приложения более доступными.
Самое важное правило ARIA: если уже существует родной HTML-элемент с нужным поведением, используйте его вместо того, чтобы воссоздавать его с помощью ARIA. <button> всегда лучше, чем <div role="button">, потому что родная кнопка даёт вам активацию с клавиатуры, фокус и семантику для программ чтения с экрана без единой строки JavaScript. ARIA не добавляет никакого поведения — она только изменяет то, как программа чтения с экрана описывает элемент. Обращайтесь к ARIA, когда ни один родной элемент не подходит (пользовательские вкладки, ползунки, древовидные представления, живые регионы).
Использование атрибутов ARIA для улучшения доступности
Роли ARIA
Роли ARIA определяют тип элемента, помогая вспомогательным технологиям понять его назначение.
<div role="button" aria-pressed="false">Toggle</div>Этот неинтерактивный элемент использует состояние aria-pressed для указания статуса переключения.
Свойства и состояния ARIA
Свойства и состояния ARIA предоставляют дополнительную информацию об элементах.
<!DOCTYPE html>
<html>
<head>
<title>ARIA Example</title>
</head>
<body>
<div role="alert" id="live-region">
<!-- Dynamic content goes here -->
</div>
<script>
document.getElementById('live-region').textContent = "This is an important message.";
</script>
</body>
</html>Этот пример использует свойства ARIA для создания живого региона, который автоматически объявляет важные сообщения. Живой регион — это элемент, изменения которого программа чтения с экрана зачитывает вслух автоматически, без необходимости перемещать на него фокус — идеально подходит для обновлений статуса, ошибок форм или сообщений в чате.
Вы управляете срочностью объявления об изменении:
role="alert"(илиaria-live="assertive") немедленно прерывает пользователя — используйте только для ошибок и срочных сообщений.aria-live="polite"ждёт паузы в действиях пользователя перед объявлением — используйте для несрочных статусов, например «Товар добавлен в корзину».
<div aria-live="polite" id="status"></div>
<script>
// Updating the text content triggers the announcement
document.getElementById('status').textContent = 'Settings saved.';
</script>Элемент живого региона должен уже существовать в DOM до того, как вы его обновляете; если вы добавляете элемент и его текст одновременно, многие программы чтения с экрана пропустят изменение.
Лучшие практики
- Используйте семантический HTML: всегда предпочитайте семантические HTML-элементы для придания контенту чёткого смысла и структуры.
- Обеспечьте доступность с клавиатуры: убедитесь, что все интерактивные элементы доступны и управляемы с клавиатуры.
- Эффективно управляйте фокусом: контролируйте фокус программно, чтобы направлять пользователей при динамических изменениях контента.
- Используйте ARIA разумно: применяйте роли, свойства и состояния ARIA для улучшения, а не замены семантики родных HTML-элементов.
- Тестируйте с вспомогательными технологиями: регулярно проверяйте веб-приложения с помощью программ чтения с экрана и других вспомогательных технологий для обеспечения доступности.
- Используйте инструменты автоматического тестирования: запускайте проверки с помощью таких инструментов, как axe или Lighthouse, чтобы выявлять распространённые проблемы доступности на ранних этапах.
Всегда обеспечивайте доступность модальных и других динамических элементов с помощью эффективного управления фокусом. Используйте JavaScript для удержания фокуса внутри модальных окон, циклически переключая фокусируемые элементы с помощью клавиши Tab, чтобы пользователи клавиатуры случайно не вышли за пределы диалога. Это улучшает доступность и обеспечивает лучший пользовательский опыт.
Связанные темы
- Выбор элементов DOM — найдите элементы, которым нужны метки и фокус.
- Изменение документа — задавайте атрибуты ARIA и текст
altпри создании элементов. - Обработка событий в DOM — добавляйте обработчики
clickиkeydownдля поддержки клавиатуры. - Фокусировка: focus / blur — управляйте фокусом программно для модальных окон и динамического контента.
Заключение
Доступность — это фундаментальный аспект веб-разработки, который гарантирует, что ваш контент будет пригоден для использования всеми людьми, независимо от их возможностей. Создавая доступный контент, применяя методы для обеспечения доступности манипуляций с DOM и используя атрибуты ARIA, вы можете значительно улучшить инклюзивность и удобство использования своих веб-приложений. Соблюдение этих практик не только помогает выполнять правовые стандарты, но и улучшает общий пользовательский опыт.