Некоторые действия в веб-приложении требуют переключения внимания пользователя. Мы будем использовать пакет focus-trap, чтобы перехватывать фокус внутри узла DOM или компонента React.
При разработке интерактивных приложений для современной сети изменения заключаются в том, что вам потребуется создавать элементы наложения. Когда такие элементы активны, остальная часть страницы обычно визуально скрыта, и пользователю разрешено взаимодействовать только с этим элементом. Мы можем использовать ARIA-атрибуты, чтобы помочь со вспомогательными технологиями, но это не помешает пользователям клавиатуры выйти из активного элемента.
Например, установка aria-modal="true"` в активном модальном окне сообщит вспомогательным технологиям, что элементы под текущим диалоговым окном недоступны для взаимодействия (https://www.w3.org/TR/wai- aria-practices/examples/dialog-modal/dialog.html). Это, однако, не помешает пользователю выйти из вашего модального компонента. В этом случае нам придется прибегнуть к JavaScript и перехватить фокус клавиатуры и сохранить элемент с вкладками в нашем модальном оверлее.
Перехватывайте фокус с помощью ванильного JavaScript
Перехватывать фокус и оставаться доступным — довольно сложная и хрупкая функциональность (https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role#Required_JavaScript_features). Итак, мы собираемся использовать хорошо написанную ловушку фокуса, чтобы помочь реализовать доступный модальный компонент.
Допустим, у нас есть следующая HTML-разметка, содержащая модальное окно.
<div id="demo">
<button id="show">show modal</button>
<div id="modal">
Modal with <a href="#">with</a> <a href="#">some</a>
<a href="#">focusable</a> elements.
<button id="hide">hide modal</button>
</div>
</div>
С помощью скрипта, прикрепленного к HTML-странице, мы можем перехватывать фокус при отображении модального окна.
import { createFocusTrap } from 'focus-trap'
const modal = document.getElementById('modal')
const focusTrap = createFocusTrap('#modal', {
onActivate: function () {
modal.className = 'trap is-visible'
},
onDeactivate: function () {
modal.className = 'trap'
},
})
document.getElementById('show').addEventListener('click', function () {
focusTrap.activate()
})
document.getElementById('hide').addEventListener('click', function () {
focusTrap.deactivate()
})
Вызываемый пакет focus-trap
предоставляет гораздо больше возможностей. Например, деактивация при нажатии за пределами компонента или деактивация триггера при нажатии клавиши ESC
.
Перехватывайте фокус с помощью React
Чтобы добиться ловушки фокуса с помощью React, мы можем использовать пакет focus-trap-react. Это тонкая обертка вокруг оригинальной упаковки.
Давайте преобразуем наш ванильный пример JavaScript в компонент React.
import React from 'react'
import ReactDOM from 'react-dom'
import FocusTrap from 'focus-trap-react'
const Demo = () => {
const [showModal, setShowModal] = React.useState(false)
return (
<div>
<button onClick={() => setShowModal(true)}>show modal</button>
<FocusTrap active={showModal}>
<div id="modal">
Modal with <a href="#">with</a> <a href="#">some</a>{' '}
<a href="#">focusable</a> elements.
<button onClick={() => setShowModal(false)}>
hide modal
</button>
</div>
</FocusTrap>
</div>
)
}
ReactDOM.render(<Demo />, document.getElementById('demo'))
Вы заметите, что мы обернули модальное окно компонентом <FocusTrap />
. Всякий раз, когда ловушка активна — на что указывает состояние showModal
— только дочерние элементы внутри ловушки получат фокус.
Заключительные мысли
Довольно сложно разработать модальный или диалоговый компонент, доступный любому из ваших пользователей. Особенно, когда они слабовидящие и используют клавиатуру для навигации по вашему приложению. Добавив пакет focus-trap в свой набор инструментов, становится намного проще создавать компоненты с навигацией с помощью клавиатуры, не оставляя пользователей позади.