7.2: Menu Bars
Menu bars are typically presented horizontally across the top of a website or web application. They contain links to key areas of the website or application. They function as toggles that open submenus or function as both links and toggles. Menu bars remain in view across the entire website or application.
Roles, states, and properties used in a menu bar
- aria-hidden = [true|false]
- role = "menubar"
- role = "menu"
- role = "menuitem"
- aria-labelledby = "[instruction div id]"
- aria-label = [link text]
- tabindex = [0 | -1]
- aria-haspopup = "true"
- aria-expanded = "[true|false]"
- aria-selected = "[true|false]"
The following JSFiddle presents a typical menu bar widget with a variety of sub menus. Review the JavaScript and HTML markup. Test the menu bar presented under the Result tab with ChromeVox to understand how it functions without any accessibility features added. You can work in JSFiddle itself by clicking the “Edit in JSFiddle” link at the top, right-hand side, copying the accessibility/WAI-ARIA code described below to fix the accessibility of the menu bar before completing Activity 12 on the page that follows.
First, provide some instructions on how to use the menu with a keyboard and add them to the default options.
Hide the instructions from screen readers until needed, adding
aria-hidden="true"
to the instructions
<div>
defined when the menu is initialized.
Add
role="menubar"
to the top level
<ul>
in the menu. Make that
<ul>
keyboard focusable with
tabindex="0"
, so it reads the instructions while in focus and referenced with
aria-labelledby
.
For all the menu items in the menu bar that have submenus, add
role="menu"
to their
<ul>
and hide them by default using
aria-hidden="true"
. This can be located after the
$elem.find('ul:eq(0)')
block presented immediately above.
Hide the links in the menu items from screen readers by default using
tabindex="-1"
and setting
aria-hidden="true"
.
Set up the menu items throughout the menu using
role="menuitem".
Also, remove keyboard access by default with
tabindex="-1"
. Next, label each menu item with the text of the associated link using
aria-label="[$link.text]"
.
For each of the menu items that has a submenu, add
aria-haspopup="true"
to announce the presence of the submenu, and set its default state to “collapsed” by adding
aria-expanded="false"
.
When a menu item is marked selected, also add
aria-selected="true"
and add keyboard access back to the menu item with
tabindex="0"
.
Add keyboard access back to menu items using
tabindex="0"
.
Reference the keyboard access class, where mouse events are defined in the
onKeyDown
function, described below.
In the
showSubMenu
function, add
aria-expanded="true"
submenus when they are expanded, remove keyboard access from the submenu container with
tabindex="-1"
. Then, make the submenu visible with
aria-hidden="false"
.
In the
hideSubMenu
function, set
aria-expanded="false"
, hide submenus with
aria-hidden="true"
, and remove keyboard access with
tabindex="-1"
when a submenu is closed.
When the
collapseAll
function is called, to collapse any open menus, reverse all attributes defining the element as open, reverting to
aria-hidden="true"
,
aria-expanded="false"
and re-adding keyboard access with
tabindex="0"
so it can be opened again.
Adding Keyboard Operability
Menu bar keyboard functionality can be complex, particularly with large menus with multiple levels of submenus, and they can include redundant keys that perform the same function. The W3C defines suggested keyboard interaction for a menu bar as follows:
Menu Bar Keyboard Interaction
This description of keyboard behaviours assumes the following:
-
A horizontal
menubar
containing severalmenuitem
elements. -
All items in the
menubar
have child submenus that contain multiple vertically arranged items. -
Some of the
menuitem
elements in the submenus have child submenus with items that are also vertically arranged.
When reading the following descriptions, also keep in mind these items:
-
Focusable elements, which may have role
menuitem
,menuitemradio
, ormenuitemcheckbox
, are referred to as items. -
If a behaviour applies to only certain types of items, e.g.,
menuitem
elements, the specific role name is used. -
Submenus, also known as pop-up menus, are elements with role
menu
. - Except where noted, menus opened from a menu button behave the same as menus opened from a menu bar.
-
When a
menu
opens, or when amenubar
receives focus, keyboard focus is placed on the first item. All items are focusable as described in 5.6 Keyboard Navigation Inside Components . -
Enter:
-
When focus is on a
menuitem
that has a submenu, opens the submenu and places focus on its first item. - Otherwise, activates the item and closes the menu.
-
When focus is on a
-
Space:
-
(Optional): When focus is on a
menuitemcheckbox
, changes the state without closing the menu. -
(Optional): When focus is on a
menuitemradio
that is not checked, without closing the menu, checks the focusedmenuitemradio
and unchecks any other checkedmenuitemradio
element in the same group. -
(Optional): When focus is on a
menuitem
that has a submenu, opens the submenu and places focus on its first item. -
(Optional): When focus is on a
menuitem
that does not have a submenu, activates themenuitem
and closes the menu.
-
(Optional): When focus is on a
-
Down Arrow:
-
When focus is on a
menuitem
in amenubar
, opens its submenu and places focus on the first item in the submenu. -
When focus is in a
menu
, moves focus to the next item, optionally wrapping from the last to the first.
-
When focus is on a
-
Up Arrow:
-
When focus is in a
menu
, moves focus to the previous item, optionally wrapping from the first to the last. -
(Optional): When focus is on a
menuitem
in amenubar
, opens its submenu and places focus on the last item in the submenu.
-
When focus is in a
-
Right Arrow:
-
When focus is in a
menubar
, moves focus to the next item, optionally wrapping from the last to the first. -
When focus is in a
menu
and on amenuitem
that has a submenu, opens the submenu and places focus on its first item. -
When focus is in a
menu
and on an item that does not have a submenu, performs the following 3 actions:- Closes the submenu and any parent menus.
-
Moves focus to the next
menuitem
in themenubar
. -
Either: (Recommended) opens the submenu of that
menuitem
without moving focus into the submenu, or opens the submenu of thatmenuitem
and places focus on the first item in the submenu.
Note that if the
menubar
were not present, e.g., the menus were opened from a menubutton, Right Arrow would not do anything when focus is on an item that does not have a submenu.
-
When focus is in a
-
Left Arrow:
-
When focus is in a
menubar
, moves focus to the previous item, optionally wrapping from the last to the first. -
When focus is in a submenu of an item in a
menu
, closes the submenu and returns focus to the parentmenuitem
. -
When focus is in a submenu of an item in a
menubar
, performs the following 3 actions:- Closes the submenu.
-
Moves focus to the previous
menuitem
in themenubar
. -
Either: (Recommended) opens the submenu of that
menuitem
without moving focus into the submenu, or opens the submenu of thatmenuitem
and places focus on the first item in the submenu.
-
When focus is in a
-
Home: If arrow key wrapping is not supported, moves focus to the first item in the current
menu
ormenubar
. -
End: If arrow key wrapping is not supported, moves focus to the last item in the current
menu
ormenubar
. - Any key that corresponds to a printable character (Optional): Move focus to the next menu item in the current menu whose label begins with that printable character.
-
Escape: Close the menu that contains focus and return focus to the element or context, e.g., menu button or parent
menuitem
, from which the menu was opened. -
Tab: Moves focus to the next element in the tab sequence, and if the item that had focus is not in a
menubar
, closes itsmenu
and all open parentmenu
containers. -
Shift + Tab: Moves focus to the previous element in the tab sequence, and if the item that had focus is not in a
menubar
, closes itsmenu
and all open parentmenu
containers.
- Disabled menu items are focusable but cannot be activated.
- A separator in a menu is not focusable or interactive.
- If a menu is opened or a menu bar receives focus as a result of a context action, Esc or Enter may return focus to the invoking context. For example, a rich text editor may have a menu bar that receives focus when a shortcut key, e.g., Alt+F10, is pressed while editing. In this case, pressing Esc or activating a command from the menu may return focus to the editor.
-
Although it is recommended that authors avoid doing so, some implementations of navigation menu bars may have
menuitem
elements that both perform a function and open a submenu. In such implementations, Enter and Space bar perform a navigation function, e.g., load new content, while Down Arrow, in a horizontal menu bar, opens the submenu associated with that samemenuitem
. -
When items in a
menubar
are arranged vertically and items inmenu
containers are arranged horizontally:- Down Arrow performs as Right Arrow is described above, and vice versa.
- Up Arrow performs as Left Arrow is described above, and vice versa.
Source: W3C WAI-ARIA 1.1 Authoring Practices
Here we have implemented a subset of the keyboard interaction W3C recommends in an
onKeyDown()
function that is called when event handlers are set up for menu items. These keys include
Left
and
Right arrows
,
Up
and
Down arrows
, the
Space bar
and
Enter
keys, and
Tab
and
Esc
keys. Copy the following function into the ik_menu.js file, near the end, to add keyboard operability to the menu.
Accessible Menu Bar in Action
Watch the following video showing ChromeVox interacting with a menu bar. The Tab key is used to navigate into the menu bar, to the first menu item, and to exit the menu bar. Left and Right arrow keys are used to move across the top level menu items. Up and Down arrows are used to move into and out of a submenu and to move between menu items in a submenu. The Space bar or Enter key are used to activate a menu item. The Esc key closes the current submenu. Aim to have the menu bar you update in the activity on the next page operate and announce itself like the one in the video.
Video: Accessible Menu Bar