Кратко
СкопированоСамостоятельная роль виджета из 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. Анатомия элемента в виде схемы:
