6. Interactive WAI-ARIA (Advanced)
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.
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