Изменение порядка DOM с помощью tabindex
Порядок табуляции по умолчанию, обеспечиваемый положением собственных элементов DOM, удобен, но бывают случаи, когда вам нужно изменить порядок табуляции, а физическое перемещение элементов в HTML не всегда является оптимальным или даже осуществимым решением. . В этих случаях вы можете использовать HTML-атрибут tabindex
, чтобы явно установить положение табуляции элемента.
tabindex
может применяться к любому элементу (хотя он не обязательно полезен для каждого элемента) и принимает диапазон целочисленных значений. Используя tabindex
, вы можете указать явный порядок для фокусируемых элементов страницы, вставить в порядок табуляции элемент, который в противном случае не мог бы быть фокусируемым, и удалить элементы из порядка табуляции. Например:
tabindex="0"
: вставляет элемент в естественный порядок табуляции. Элемент можно сфокусировать, нажав клавишу Tab
, а элемент можно сфокусировать, вызвав его метод focus()
<custom-button tabindex="0">Press Tab to Focus Me!</custom-button>
tabindex="-1"
: удаляет элемент из естественного порядка табуляции, но элемент все равно можно сфокусировать, вызвав его метод focus()
// TODO: DevSite - Code sample removed as it used inline event handlers
// TODO: DevSite – удален пример кода, поскольку в нем использовались встроенные обработчики событий
tabindex="5"
: любой tabindex больше 0 перемещает элемент в начало естественного порядка табуляции. Если существует несколько элементов с индексом табуляции больше 0, порядок табуляции начинается с наименьшего значения, большего нуля, и идет вверх. Использование tabindex больше 0 считается антишаблоном .
<button>I should be first</button>
<button>And I should be second</button>
<button tabindex="5">But I jumped to the front!</button>
Это особенно верно в отношении невводных элементов, таких как заголовки, изображения или заголовки статей. Добавление tabindex
к элементам такого типа является контрпродуктивным. Если возможно, лучше всего расположить исходный код так, чтобы последовательность DOM обеспечивала логический порядок табуляции. Если вы используете tabindex
, ограничьте его пользовательскими интерактивными элементами управления, такими как кнопки, вкладки, раскрывающиеся списки и текстовые поля; то есть элементы, для которых пользователь может ожидать ввода данных.
Не беспокойтесь о том, что пользователи программ чтения с экрана пропустят важный контент из-за отсутствия tabindex
. Даже если контент очень важен, например изображение, если пользователь не может с ним взаимодействовать, нет смысла делать его фокусируемым. Пользователи программ чтения с экрана по-прежнему смогут понять содержимое изображения, если вы обеспечите правильную поддержку атрибута alt
, о чем мы вскоре расскажем.
Управление фокусом на уровне страницы
Вот сценарий, в котором tabindex
не только полезен, но и необходим. Возможно, вы создаете надежную одну страницу с разными разделами контента, не все из которых видны одновременно. На страницах такого типа щелчок по навигационной ссылке может изменить видимый контент без обновления страницы.
В этом случае вам, вероятно, придется идентифицировать выбранную область содержимого, присвоить ей tabindex
табуляции -1, чтобы она не отображалась в естественном порядке табуляции, и вызвать ее метод focus
. Этот метод, называемый управлением фокусом , синхронизирует воспринимаемый пользователем контекст с визуальным контентом сайта.
Управление фокусом в компонентах
Управление фокусом при изменении чего-либо на странице важно, но иногда вам нужно управлять фокусом на уровне элемента управления — например, если вы создаете собственный компонент.
Рассмотрим собственный элемент select
. Он может получить базовый фокус, но, оказавшись там, вы можете использовать клавиши со стрелками, чтобы открыть дополнительные функции (выбираемые параметры). Если бы вы создавали собственный элемент select
, вам бы хотелось предоставить такое же поведение, чтобы пользователи, которые в основном полагаются на клавиатуру, могли взаимодействовать с вашим элементом управления.
<!-- Focus the element using Tab and use the up/down arrow keys to navigate -->
<select>
<option>Aisle seat</option>
<option>Window seat</option>
<option>No preference</option>
</select>
Понять, какое поведение клавиатуры следует реализовать, может быть сложно, но есть полезный документ, к которому вы можете обратиться. В руководстве «Практика разработки доступных полнофункциональных интернет-приложений (ARIA)» перечислены типы компонентов и типы действий с клавиатуры, которые они поддерживают. Мы рассмотрим ARIA более подробно позже, а сейчас давайте воспользуемся руководством, которое поможет нам добавить поддержку клавиатуры в новый компонент.
Возможно, вы работаете над новыми пользовательскими элементами , напоминающими набор переключателей, но с вашим уникальным внешним видом и поведением.
<radio-group>
<radio-button>Water</radio-button>
<radio-button>Coffee</radio-button>
<radio-button>Tea</radio-button>
<radio-button>Cola</radio-button>
<radio-button>Ginger Ale</radio-button>
</radio-group>
Чтобы определить, какая поддержка клавиатуры им нужна, вы можете просмотреть руководство ARIA Authoring Practices . Раздел 2 содержит список шаблонов проектирования, и в этом списке находится таблица характеристик радиогрупп — существующего компонента, который наиболее точно соответствует вашему новому элементу.
Как видно из таблицы, одним из распространенных вариантов поведения клавиатуры, которое должно поддерживаться, являются клавиши со стрелками вверх/вниз/влево/вправо. Чтобы добавить такое поведение к новому компоненту, мы воспользуемся методом перемещения tabindex .
Перемещающийся tabindex работает, устанавливая tabindex
в -1 для всех дочерних элементов, кроме активного в данный момент.
<radio-group>
<radio-button tabindex="0">Water</radio-button>
<radio-button tabindex="-1">Coffee</radio-button>
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
Затем компонент использует прослушиватель событий клавиатуры, чтобы определить, какую клавишу нажимает пользователь; когда это происходит, он устанавливает табиндекс ранее сфокусированного дочернего tabindex
на -1, устанавливает табиндекс дочернего tabindex
который должен быть сфокусирован, на 0 и вызывает для него метод фокуса.
<radio-group>
// Assuming the user pressed the down arrow, we'll focus the next available child
<radio-button tabindex="-1">Water</radio-button>
<radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
Когда пользователь достигает последнего (или первого, в зависимости от направления перемещения фокуса) дочернего элемента, вы снова выполните цикл и фокусируете первого (или последнего) дочернего элемента.
Вы можете попробовать готовый пример ниже. Проверьте элемент в DevTools, чтобы увидеть, как tabindex перемещается от одного радио к другому.
// TODO: DevSite – удален пример кода, поскольку в нем использовались встроенные обработчики событий
Вы можете просмотреть полный исходный код этого элемента на GitHub.
Модалы и ловушки клавиатуры
Иногда, когда вы управляете концентрацией внимания, вы можете попасть в ситуацию, из которой не сможете выбраться. Рассмотрим виджет автозаполнения, который пытается управлять фокусом и фиксирует поведение вкладки, но не позволяет пользователю покинуть ее, пока она не будет завершена. Это называется ловушкой клавиатуры , и это может очень расстраивать пользователя. Раздел 2.1.2 контрольного списка Web AIM решает эту проблему, утверждая, что фокус клавиатуры никогда не должен блокироваться или захватываться одним конкретным элементом страницы . Пользователь должен иметь возможность переходить ко всем элементам страницы и обратно, используя только клавиатуру.
Как ни странно, бывают случаи, когда такое поведение действительно желательно, например, в модальном окне. Обычно, когда модальное окно отображается, вы не хотите, чтобы пользователь имел доступ к содержимому, находящемуся за ним. Вы можете добавить наложение, чтобы визуально закрыть страницу, но это не помешает фокусу клавиатуры случайно выйти за пределы модального окна.
В подобных случаях вы можете реализовать временную ловушку клавиатуры, чтобы гарантировать, что вы захватываете фокус только во время отображения модального окна, а затем восстанавливаете фокус на ранее сфокусированный элемент, когда модальное окно закрыто.
Есть несколько предложений о том, как упростить эту задачу для разработчиков, включая элемент
<dialog>
, но они пока не имеют широкой поддержки браузеров.См. эту статью MDN для получения дополнительной информации о
<dialog>
и этот модальный пример для получения дополнительной информации о модальных окнах.
Рассмотрим модальный диалог, представленный элементом div
, содержащим несколько элементов, и другим div
, представляющим наложение фона. Давайте пройдемся по основным шагам, необходимым для реализации временной ловушки клавиатуры в этой ситуации.
- Используя
document.querySelector
, выберите модальные и наложенные элементы div и сохраните их ссылки. - При открытии модального окна сохраните ссылку на элемент, который был в фокусе при открытии модального окна, чтобы вы могли вернуть фокус на этот элемент.
- Используйте прослушиватель нажатия клавиш , чтобы захватывать клавиши по мере их нажатия, когда модальное окно открыто. Вы также можете прослушать щелчок по фоновому наложению и закрыть модальное окно, если пользователь нажмет на него.
- Затем получите коллекцию фокусируемых элементов в модальном окне. Первый и последний фокусируемые элементы будут действовать как «часовые», сообщая вам, когда нужно зациклить фокус вперед или назад, чтобы оставаться внутри модального окна.
- Отобразите модальное окно и сфокусируйте первый фокусируемый элемент.
- Когда пользователь нажимает
Tab
илиShift+Tab
, перемещайте фокус вперед или назад, циклически переходя к последнему или первому элементу в зависимости от ситуации. - Если пользователь нажимает
Esc
, закройте модальное окно. Это очень полезно, поскольку позволяет пользователю закрывать модальное окно без поиска конкретной кнопки закрытия, и это приносит пользу даже пользователям, использующим мышь. - Когда модальное окно закрыто, скройте его и фоновое наложение и восстановите фокус на ранее сохраненном ранее элементе.
Эта процедура дает вам удобное и удобное модальное окно, которое каждый может эффективно использовать.
Для получения более подробной информации вы можете изучить этот пример кода и просмотреть живой пример на завершенной странице .