Некоторые действия в веб-приложении требуют переключения внимания пользователя. Мы будем использовать пакет 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 в свой набор инструментов, становится намного проще создавать компоненты с навигацией с помощью клавиатуры, не оставляя пользователей позади.