Мы привыкли слышать, что Angular - это фреймворк, который решает массу задач из коробки: свой CLI, встроенная сборка приложений, автоматическая миграция на новые версии с помощью schematic, работа с HTTP, DI, реактивные формы, работа с состоянием - все это удобные инструменты для разработчика. Обычно я сравниваю его с коробкой автомат: сел и поехал, не отвлекаясь на переключение передач.
Но в мире веба мы всегда должны думать о пользователях. И один из разделов, который заботится о них, называется доступность (Accessibility, A11y в англоязычной среде). И тут Angular позаботился о нас и дал мощнейший инструмент из коробки под названием CDK A11y. Предлагаю ознакомиться с концепцией доступности и изучить применение этого инструмента в Angular.
Что такое доступность
Существует множество способов взаимодействия с сайтом: мышь, клавиатура, голосовое управление, touch-устройства и т.д. Доступным будет считаться тот сайт, который максимально покрывает все варианты взаимодействия с ним. Независимо от пользовательских предпочтений, возможность дойти от точки действия А к Б не будет являться для него проблемой. Попробуйте навигацию клавиатурой по Github - в нем неплохо учтены пользовательские кейсы доступности.
Помимо способов взаимодействия с сайтом, в A11y можно выделить отдельное направление про контент на сайте. Сюда будут входить хорошие шрифты, цвета (возможны высокие контрасты для людей с нарушением зрения), скрытый/вспомогательный контент.
Также к доступности можно отнести: скорость загрузки сайта (возможно, есть пользователи, у которых ограничена пропускная способность интернета). А также работу сайта на различных устройствах.
Тема доступности очень важна для пользователей, ведь это связано с удобством. Это как доступная среда на улице: вход в подъезд без ступеней и лишних препятствий, велодорожки, светофоры и т.д. Более того, в некоторых странах доступность начинает входит в стандарты сайта по умолчанию, и в случае отсутствия, к владельцу могут быть применены штрафы. Кроме того, Google при индексации сайтов может учитывать и проверять состояние доступности на сайте.
CDK A11y в Angular
Начнем с FocusTrap. Это API предназначено, чтобы захватывать фокус в рамках определенного участка интерфейса. Такой захват нужен в случае, если нужно обеспечить навигацию через TAB.
Обычно выделяют следующие кейсы в интерфейсе для использования захвата фокуса:
навигация в модальных окнах;
навигация в меню;
навигация в datepicker;
различные пользовательские формы ввода, например, форма login;
Пример реализации захвата в календаре от Material.
Использовать cdkFocusTrap просто - достаточно обернуть необходимую область для захвата и при перемещении по tab мы не будем вываливаться за пределы области.
Area 1
<div cdkTrapFocus class="example-cdkTrapFocus">
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
<button>button 4</button>
</div>
Area 2
<div class="example-cdkTrapFocus">
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
<button>button 4</button>
</div>
FocusMonitor - это сервис, который предоставляет работу с фокусом. Он дает две возможности:
1. С помощью Monitor можно подписаться на Observable и получать состояния DOM элемента. Он отдаёт события focus и blur и возвращает, с помощью чего был установлен фокус (мышь, клавиатура, touch-устройство или установка через код)
В каких кейсах можно использовать FocusMonitor? Где угодно! Например:
скрытие / открытие tooltip, пример из material;
выполнение логики для перерисовки визуальных частей компонента таблиц при взаимодействии с компонентами сортировки, пример из material;
мониторинг списка с выделением, пример из material;
Пример использования в коде:
export class FocusMonitorExample implements AfterContentInit, OnDestroy {
....
constructor(
private _element: ElementRef<HTMLElement>,
private _focusMonitor: FocusMonitor
) {}
ngAfterContentInit(): void {
this._focusMonitor?.monitor(this._element)
.pipe(takeUntil(this._destroyed))
.subscribe(origin => {
if (origin === 'keyboard' || origin === 'program') {
// выполняем логику компонента
}
});
}
ngOnDestroy() {
this._focusMonitor?.stopMonitoring(this._element);
}
}
2. Он позволяет устанавливать фокус на указанные элементы через метод focusVia. При этом, в него можно передать параметр, с помощью чего был установлен фокус (мышь, клавиатура, через touch, через код)
В каких кейсах можно использовать FocusMonitor для установки фокуса:
кнопка в форме - кнопка, которая получает фокус. Такую кнопку можно нажать сразу, без перевода на неё фокуса с помощью клавиатуры. Отличный кейс, когда требуется показывать на сайте onboarding/tutorial с кнопкой «далее»;
поле ввода - при создании письма в gmail фокус, фокус сразу попадает в поле ввода почты получателя;
чекбоксы, радиобаттоны на форме, можно управлять фокусом элемента по умолчанию;
элементы меню;
Использование в коде:
export class FocusMonitorViaExample {
...
@ViewChild('btn') buttonElement: ElementRef;
constructor(
private _focusMonitor: FocusMonitor
) {}
focusElement() {
this._focusMonitor.focusVia(this.buttonElement, 'program');
}
}
Если всё так просто – зачем нам FocusMonitor?
Может показаться, что задачи сервиса довольно простые: обрабатывать события blur, focus или устанавливать фокус. Но данный сервис избавляет нас от рутины и потенциальных багов, которые могут возникнуть в ходе работы приложения. Можно выделить следующие плюсы:
Переиспользуемый код. Так или иначе мы бы сами сделали что-то подобное для своих приложений, чтобы не дублировать код.
Сервис работает в разном окружении, если приложение запущено за пределами браузера, эти кейсы предусмотрены (тесты, ssr и т.д.).
Обработка ShadowRoot. Если элемент будет находиться внутри ShadowRoot, то обработчики focus/blur нужно привязывать к его корню, иначе мы не получим события.
Оптимизация работы приложения. Используется runOutsideAngular для работы с нативными событиями addEventListener, чтобы лишний раз не тревожить Zone.js.
Предусматривает мониторинг дочерних элементов.
Поэтому CDK FocusMonitor очень полезен в ваших приложениях. Многие задачи, с которыми вы можете столкнуться, уже решены в нём.
Заключение
Мы рассмотрели понятие доступности. Узнали, какие инструменты существуют в Angular CDK. Подробно рассмотрели FocusMonitor и FocusTrap API для работы с доступностью. Но это далеко не весь функционал, который предоставляет нам CDK. Тема довольно обширная и выходит за рамки одной статьи. Если у вас есть опыт работы с остальным API доступности, напишите в комментариях, интересен ваш опыт и видение в этом направлении.