Кратко
СкопированоСамостоятельная роль виджета из WAI-ARIA для строки меню, которая обычно встречается в операционных системах, программах и веб-приложениях.
В HTML нет тега с ролью menubar
.
Пример
Скопировано<div role="menubar"> <button role="menuitem" type="button" aria-expanded="false" aria-controls="fonts" aria-haspopup="menu" > Начертание </button> <ul role="menu" id="fonts" tabindex="-1" > <!-- Содержимое подменю --> </ul> <!-- Остальные элементы строки меню --></div>
<div role="menubar"> <button role="menuitem" type="button" aria-expanded="false" aria-controls="fonts" aria-haspopup="menu" > Начертание </button> <ul role="menu" id="fonts" tabindex="-1" > <!-- Содержимое подменю --> </ul> <!-- Остальные элементы строки меню --> </div>
Как пишется
СкопированоЗадайте любому HTML-тегу атрибут role
, лучше всего <div>
или <ul>
.
Строка меню, как и любая другая навигация, должна содержать как минимум один элемент. Это может быть обычный пункт menuitem
, дополнительно раскрывающий подменю menu
, пункт в виде чекбокса menuitemcheckbox
или пункт в виде радиокнопки menuitemradio
.
Пункты могут располагаться отдельно или объединяться в группы с ролью group
. Когда в строке несколько групп, их можно отделить друг от друга обычными (неинтерактивными) разделителями с ролью separator
.
<ul role="menubar"> <span role="group"> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="true" tabindex="0" > Показать превью </span> </li> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="false" tabindex="-1" > Показать неразрывные пробелы </span> </li> </span> <span role="separator" aria-orientation="vertical" > </span> <li role="presentation"> <span role="menuitem" tabindex="-1" > Сохранить </span> </li> <!-- Остальные элементы --></ul>
<ul role="menubar"> <span role="group"> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="true" tabindex="0" > Показать превью </span> </li> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="false" tabindex="-1" > Показать неразрывные пробелы </span> </li> </span> <span role="separator" aria-orientation="vertical" > </span> <li role="presentation"> <span role="menuitem" tabindex="-1" > Сохранить </span> </li> <!-- Остальные элементы --> </ul>
У menubar
есть свойство aria
со значением horizontal
по умолчанию. Благодаря значению пользователи скринридеров и других вспомогательных технологий знают, что могут перемещаться по пунктам клавишами со стрелками влево ← и вправо →.
Также можете задавать menubar
все глобальные ARIA-атрибуты и несколько специальных атрибутов виджетов:
aria
, когда все элементы строки неактивны, но на них можно сделать фокус;- disabled aria
, когда нужно рассказать о выбранном пункте из строки или связанного подменю.- activedescendant
У строки может быть имя — её краткое название. Если оно видно всем, используйте aria
. Когда оно доступно только скринридерам, задайте aria
.
<ul role="menubar" aria-labelledby="label"> <span id="label">Редактор кода</span> <!-- Элементы строки меню --></ul><ul role="menubar" aria-label="Редактор кода"> <!-- Элементы строки меню --></ul>
<ul role="menubar" aria-labelledby="label" > <span id="label">Редактор кода</span> <!-- Элементы строки меню --> </ul> <ul role="menubar" aria-label="Редактор кода" > <!-- Элементы строки меню --> </ul>
Строка меню — составной виджет. Это означает, что у него особая навигация с клавиатуры, над которой придётся немного попотеть 🥵
На строку попадают с помощью клавиши Tab или сочетания Shift Tab. Когда оказались на ней в первый раз, фокус должен установиться на первом пункте, а в последующем — на последнем активном элементе. Когда находитесь на пункте и нажали на Tab, фокус перемещается на следующий интерактивный элемент после строки меню, на Tab Shift — на предыдущий. Если при этом в строке открыто подменю, оно закрываются.
Между пунктами горизонтальной строки перемещаются клавишами со стрелками влево ← и вправо →. Если она вертикальная и элементы расположены друг под другом, по ним проходят стрелками вверх ↑ и вниз ↓. Также клавиша Home должна переносить на первый пункт строки, End — на последний.
Отдельно поработайте над навигацией стрелками, когда раскрыто одно подменю из нескольких. При переходе к следующему или предыдущему элементам, связанное с ними подменю автоматически разворачивается, а предыдущее закрывается. В фокусе может оказаться раскрывающий его пункт строки или первый элемент в подменю.
Когда нажали на Enter или стрелку вниз ↓ на пункте с ролью menuitem
, раскрывается связанное с ним подменю и фокус устанавливается на первом пункте из него. Если в фокусе радиокнопки menuitemradio
или чекбоксы menuitemcheckbox
, Enter выбирает их или отменяет предыдущий выбор.
Дополнительно можете поддерживать и пробел. Он делает то же, что и Enter: раскрывает подменю или выбирает и отменяет выбор чекбокса или радиокнопки.
Не обязательно, но при фокусе на строке можно отслеживать нажатие на клавиши с буквами и символами. Пользователи смогут быстро переместиться к нужным пунктам, которые начинаются со знака с нажатой клавиши. Например, попасть на пункт «Настройки» при нажатии на клавишу H.
Управление фокусом
СкопированоДля правильной навигации в строке меню не обойтись без HTML-атрибута tabindex
. Это особенно важно, когда создаёте кастомные элементы на тегах, с которыми обычно не могут взаимодействовать пользователи. К примеру, <span>
и <div>
.
Только у одного пункта из menubar
может быть tabindex
— у первого элемента до того, как на строке сделали фокус, и у текущего пункта в фокусе. Остальные пункты должны быть с tabindex
, пока их не выбрали. В том числе отрицательный tabindex
должен быть у закрытого пока подменю.
<ul role="menubar"> <li role="presentation"> <span role="menuitem" tabindex="0" > Прикрепить картинку </span> </li> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="false" tabindex="-1" > Показать превью </span> </li> <li role="presentation"> <span role="menuitem" aria-expanded="false" aria-controls="color" aria-haspopup="menu" tabindex="-1" > Цвет </span> </li> <ul role="menu" id="color" tabindex="-1" > <!-- Содержимое подменю --> </ul></ul>
<ul role="menubar"> <li role="presentation"> <span role="menuitem" tabindex="0" > Прикрепить картинку </span> </li> <li role="presentation"> <span role="menuitemcheckbox" aria-selected="false" tabindex="-1" > Показать превью </span> </li> <li role="presentation"> <span role="menuitem" aria-expanded="false" aria-controls="color" aria-haspopup="menu" tabindex="-1" > Цвет </span> </li> <ul role="menu" id="color" tabindex="-1" > <!-- Содержимое подменю --> </ul> </ul>
Один из многочисленных вариантов решения на JavaScript:
const menuItems = Array.from(document.querySelectorAll('span[data-item]'))let lastFocusedItem = nulllet currentFocusedButtonIndex = -1menuItems.forEach((item, index) => { item.addEventListener('focus', () => { if (lastFocusedItem && lastFocusedItem !== item) { lastFocusedItem.setAttribute('tabindex', '-1') } item.setAttribute('tabindex', '0') lastFocusedItem = item })})
const menuItems = Array.from(document.querySelectorAll('span[data-item]')) let lastFocusedItem = null let currentFocusedButtonIndex = -1 menuItems.forEach((item, index) => { item.addEventListener('focus', () => { if (lastFocusedItem && lastFocusedItem !== item) { lastFocusedItem.setAttribute('tabindex', '-1') } item.setAttribute('tabindex', '0') lastFocusedItem = item }) })
Как понять
СкопированоОбычное меню на сайтах состоит из ссылок, поэтому достаточно использовать <ul>
внутри <nav>
.
В классической, «настоящей» строке меню размещают кнопки, чекбоксы, радиокнопки и другие интерактивные элементы, которые раскрывают подменю и изменяют внешний вид и содержимое других элементов на странице. В этом случае и пригодится роль menubar
. Анатомия элемента в виде схемы: