Menu bars are typically presented horizontally across the top of a website or web application, containing links to key areas of the website or application, or function as toggles that open submenus, or both links and toggle, and remain in view across the entire website or application.
Roles, states, and properties used in a menu bar
The following JSFiddle presents a typical menu bar widget with a variety of sub menus. Review the JavaScript and HTML markup, and 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” 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.Skip JSFiddle
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 the role="menubar"
to the top level <UL>
in the menu, and make that UL keyboard focusable with tabindex="0"
so it reads the instructions while in focus being 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"
and 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"
. And 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.
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:
The following description of keyboard behaviours assumes:
menubar
containing several menuitem
elements.menubar
have child submenus that contain multiple vertically arranged items.menuitem
elements in the submenus have child submenus with items that are also vertically arranged.When reading the following descriptions, also keep in mind that:
menuitem
, menuitemradio
, or menuitemcheckbox
, are referred to as items.menuitem
elements, the specific role name is used.menu
.menu
opens, or when a menubar
receives focus, keyboard focus is placed on the first item. All items are focusable as described in 5.6Keyboard Navigation Inside Components.menuitem
that has a submenu, opens the submenu and places focus on its first item.menuitemcheckbox
, changes the state without closing the menu.menuitemradio
that is not checked, without closing the menu, checks the focused menuitemradio
and unchecks any other checked menuitemradio
element in the same group.menuitem
that has a submenu, opens the submenu and places focus on its first item.menuitem
that does not have a submenu, activates the menuitem
and closes the menu.menuitem
in a menubar
, opens its submenu and places focus on the first item in the submenu.menu
, moves focus to the next item, optionally wrapping from the last to the first.menu
, moves focus to the previous item, optionally wrapping from the first to the last.menuitem
in a menubar
, opens its submenu and places focus on the last item in the submenu.menubar
, moves focus to the next item, optionally wrapping from the last to the first.menu
and on a menuitem
that has a submenu, opens the submenu and places focus on its first item.menu
and on an item that does not have a submenu, performs the following 3 actions:menuitem
in the menubar
.menuitem
without moving focus into the submenu, or opens the submenu of that menuitem
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.
menubar
, moves focus to the previous item, optionally wrapping from the last to the first.menu
, closes the submenu and returns focus to the parent menuitem
.menubar
, performs the following 3 actions:menuitem
in the menubar
.menuitem
without moving focus into the submenu, or opens the submenu of that menuitem
and places focus on the first item in the submenu.menu
or menubar
.menu
or menubar
.menuitem
, from which the menu was opened.menubar
, closes its menu
and all open parent menu
containers.menubar
, closes its menu
and all open parent menu
containers.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 same menuitem
.menubar
are arranged vertically and items in menu
containers are arranged horizontally: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.
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. The Left and Right Arrow keys are used to move across the top level menu items, and 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 coming up on the next page operate and announce itself like the one in the video.
Tree menus often have the same underlying HTML structure as a menu bar, but rather than being arranged in a horizontal layout, they tend to be arranged vertically.
WAI-ARIA roles, states, and properties used in a tree menu
The following JSFiddle presents a typical tree menu widget with a few submenus. Review the JavaScript and HTML markup, and test the tree menu 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” at the top, right-hand side, copying the accessibility/WAI-ARIA code described below to fix the accessibility of the tree menu before completing Activity 13 on the page that follows.Skip JSFiddle
First define instructions on using the tree menu with a keyboard.
Within the init()
function add keyboard focus to the tree container by applying tabindex="0"
to it and label the container with the instructions created above, which gets read by screen readers when the menu initially receives focus.
Within the init()
function, hide the instructions <div>
from screen readers by default by setting aria-hidden="true"
when the tree menu is initialized.
Within the init()
function replace the unordered list semantics with tree menu semantics using role="tree"
, and give it a title using aria-labelledby
to reference the title defined in the default options.
Within the init()
function, define menu items with role="treeitem"
, remove all keyboard access by default with tabindex="-1"
, set the number of levels in the tree based on the number of parent ULs with aria-level=[number of ULs]
, set the number of tree items on a given level with aria-setsize="[number of LIs in a UL]"
, and finally define the position of each tree item within a level using aria-posinset="[child LI index]"
.
Within the init()
function, if a tree item has a submenu UL that has been opened, set aria-expanded="true"
, otherwise set aria-expanded="false"
.
Within the init()
function, for each tree item use the text of the associated <span>
element as its label. To ensure both the label and the contents of the <span>
element are not both read, assign role="presentation"
to the <span>
.
Within the init()
function, where mouse onclick
functionality is used, provide equivalent keydown
functionality, here referencing the onKeyDown
function, shown below, that defines the keys to operate the menu.
Within the init()
function, right after adding keydown
operability, make the first item in the tree menu focusable by adding tabindex="0"
to the first <li>
.
Within the selectItem()
function, set up a roving tabindex, while at the same time applying aria-selected=[true | false]
when tree items receive or lose focus.
In the toggleSubmenu()
function announce the state of submenus to the screen reader by toggling the aria-expanded=[true | false]
attribute when a menu is opened or closed.
Much like the menu bar described in the previous activity, keyboard operability for a tree menu can be complex, with various operations using multiple key strokes to perform the same function. W3C describe potential keyboard operation in the WAI-ARIA Authoring Practices 1.1, reproduced below.
For a vertically oriented tree:
tree
role supports the aria-activedescendant property, which provides an alternative to moving DOM focus among treeitem
elements when implementing keyboard navigation. For details, see Managing Focus in Composites Using aria-activedescendant.Source: WAI-ARIA Authoring Practices 1.1
For the tree menu created here, we’ve added in basic keyboard operability. Keyboard operation includes: Up and Down, and Left and Right Arrows for navigating within the tree, and the Enter or space bar keys to toggle submenus open or closed. The Tab key by default enters and exits the tree menu and does not need to be defined as part of the keyboard operability of the tree menu.
Watch the following video showing ChromeVox interacting with a tree menu. The Tab key is used to navigate into the tree menu, to the first tree item, and to exit the tree menu. The Up and Down Arrows are used to move between tree items. The space bar or Enter key are used to expand and collapse a tree item with a submenu. When a submenu is opened, focus moves to the first tree item in the menu. Aim to have the tree menu you update in the activity coming up on the next page operate and announce itself like the one in the video.
One of the more common types of widgets that present barriers for screen reader users are drag and drop features. These can be set up in a grid, where draggable items can be rearranged horizontally or vertically by clicking on an item and moving it to a new position in the grid. A drag and drop may also be a sortable list, where items in a list can be dragged vertically to perhaps position the more important list items near the top of the list. For drag and drop elements you may come across on the Web today, the vast majority only function with a mouse, making them inaccessible to many people who rely on a keyboard to navigate. Here we will look at a sortable list, and the WAI-ARIA and associated keyboard operability required to make that list sortable while using only a screen reader and a keyboard.
Role, states, and properties used in a sortable list
The following JSFiddle presents a typical sortable list widget. Review the JavaScript and HTML markup, and test the list 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” 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 14 on the page that follows.Skip JSFiddle
As usual, create instructions on using the sortable list with a keyboard. In this case we also want to determine which modifier key to include in the instructions. For Mac it will be the Command key, otherwise it will be the Ctrl key. Here the standard accesskey key commands will also work as the modifier and can potentially be described as well (e.g. Ctrl+Alt on Mac, or Ctrl on Windows).
Assign a redundant role="list"
to the opening UL, make the UL keyboard focusable, and attach the instruction with aria-labelledby="[instruction div id]"
so keyboard navigation details are announced when the list initially receives focus while using a screen reader.
Within the init()
function, generate the <div>
that will contain the instructions, and add aria-hidden="true"
to hide it from screen readers by default.
In the items section of the init()
function, where draggable is defined for each item in the list, add a redundant role="listitem"
, and generate a label for each item that describes the list item’s current position and that that list item is “movable.” Finally set tabindex="0"
on the first list item, and tabindex="-1"
on the other list items, to ensure a list item is focusable by default.
Where the draggable attributes are defined near the end of the init()
function, attach a keydown reference to the onKeyDown()
function to make the list draggable with a keyboard.
In the resetNumbering()
function, update the label for moved items to reflect their new position in the list using aria-label = "[new position]"
.
Keyboard operation for a drag and drop sortable list is relatively simple, compared to the menu bar and tree menu. Essentially only the Up and Down Arrow keys are needed. The standard operating system modifier keys, typically used with tabindex (e.g. Crtl+Alt, or Alt, or Ctrl), function as the modifier keys when using them in addition to the Up and Down Arrows to grab, drag, and drop a list item.
The onKeyDown()
function for the sortable list presented below, defines just up and down arrow key operability, along with a roving tabindex. W3C has not yet created a best practice for authoring keyboard interaction for drag and drop elements.
Watch the following video showing ChromeVox interacting with a sortable list. The Tab key is used to navigate into the list, and to exit the list. The Up and Down Arrows are used to move between list items. On a Mac, the Command key plus Up or Down Arrow, selects a list item and moves it to a new location. On windows the Ctrl key is used instead of Command, along with the Up or Down Arrow keys to move list items. Aim to have the sortable list you update in the activity coming up on the next page operate and announce itself like the one in the video.