Application Menus

Web application menus use the same basic structure as navigation menus. They often consist of a horizontal menu bar and use fly-out functionality. However, additional markup and keyboard behavior must be added.

Additional markup

In addition to the aria-expanded and aria-haspopup attributes, the following WAI-ARIA roles are used to provide the necessary semantics of an application menu:

  • menubar: Represents a (usually horizontal) menu bar.
  • menu: Represents a set of links or commands in a menu bar, it is used for the fly-out menus.
  • menuitem: Represents an individual menu item.
  • separator: Represents a separator between two groups of menu items in a menu.
Code snippet:
<ul role="menubar" id="appmenu"><li role="menuitem" aria-haspopup="true">
		Edit
		<ul role="menu">
			<li role="menuitem">Undo</li>
			<li role="menuitem">Redo</li>
			<li role="separator"></li>
			<li role="menuitem">Cut</li>
			<li role="menuitem">Copy</li>
			<li role="menuitem">Paste</li>
			</ul>
		</li></ul>

Functionality

Adding the WAI-ARIA roles does not automatically enable the menu’s functionality or keyboard behavior. These must be added using scripting. A detailed explanation on the WAI-ARIA attributes and keyboard behavior can be found in the WAI-ARIA Authoring Practices document (draft).

Keyboard behavior

Web application menus are expected to work like desktop application menus. For example, the left and right keys are used to iterate through the top-level items, and the up and down arrows are used to navigate submenus. Pressing the tab key focuses the next item after the menu instead of the next menu item.

To allow focus to be set to menu items by keyboard, the items are given a tabindex attribute with the value -1. The first main menu item (“File” in this example) is assigned a tabindex value of 0 which adds it to the tab order and lets the user access the menu using the keyboard.

Code snippet:
Array.prototype.forEach.call(appsMenuItems, function(el, i){
		if (0 == i) {
			el.setAttribute('tabindex', '0');
			el.addEventListener("focus", function() {
				currentIndex = 0;
			});
		} else {
			el.setAttribute('tabindex', '-1');
		}
});

Array.prototype.forEach.call(subMenuItems, function(el, i){
	el.setAttribute('tabindex', '-1');
});

Top-Level Menu Items

The following table summarizes the typical behavior of top-level menu items:

Key mapping for top-level menu items
Key Action
tab ⇥ Select the next focusable element outside of the menu.
shift ⇧ + tab ⇥ Select the previous focusable element outside of the menu.
right → Select the next top-level menu item.
left ← Select the previous top-level menu item.
return/enter ↵ Open the submenu, select first submenu item.
space
down ↓
up ↑ Open the submenu, select last submenu item.
esc Leave the menu.

The following table summarizes the typical behavior of submenu items:

Key mapping for submenu items
Key Action
tab ⇥ Close the submenu, select the next focusable element outside of the menu.
shift ⇧ + tab ⇥ Close the submenu, select the previous focusable element outside of the menu.
right → Close the submenu, select the next top-level menu item.
left ← Close the submenu, select the previous top-level menu item.
return/enter ↵ Carry out function of this item. (In this example: bring up a dialog box with the text of the chosen menu item.)
space
down ↓ Select next submenu item.
up ↑ Select previous submenu item.
esc Close the submenu, select the current top-level menu item.