Difference between revisions of "ARIA Menu"

From Level Access Web Labs
Jump to navigation Jump to search
Line 1: Line 1:
 
<html lang="en-US">
 
<html lang="en-US">
 
<head>
 
<head>
 +
<meta charset="utf-8">
 
<title>ARIA Menu Example</title>
 
<title>ARIA Menu Example</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
 +
<style>
 +
#nav a {
 +
display: block;
 +
text-decoration: none;
 +
color: black;
 +
padding: 2px 1em;
 +
}
 +
#nav a:hover, #nav a:focus {
 +
  background-color: #340449 !important;
 +
  color: white !important;
 +
  outline: none;
 +
}
 +
ul.menubar {
 +
  margin: 0;
 +
padding: 0 .25em;
 +
list-style: none;
 +
font-size: 1em;
 +
height: 1.85em;
 +
width: 50em;
 +
border: 1px solid black;
 +
background: #ddd;
 +
}
 +
ul.menubar > li {
 +
float: left;
 +
display: inline; position: relative;
 +
margin: 0;
 +
padding: .25em .35em;
 +
height: 1.25em;
 +
width: 10em;
 +
}
 +
ul.menu {
 +
position: absolute;
 +
left: 0;
 +
top: 1.75em;
 +
display: none;
 +
list-style: none;
 +
margin: 0;
 +
padding: 0;
 +
font-weight: normal;
 +
font-size: 100%;
 +
border: 1px solid black;
 +
background-color: #ccc;
 +
width: 10em;
 +
}
 +
ul.menu li {
 +
float: none;
 +
display: block;
 +
white-space: nowrap;
 +
}
 +
li.menu-hover, li.menu-focus {
 +
background-color: #340449 !important;
 +
color: white;
 +
outline: none;
 +
}
 +
li.menu-hover > a, li.menu-focus > a {
 +
color: white !important;
 +
outline: none;
 +
}
 +
</style>
 +
<script type="text/javascript">
 +
$(document).ready(function() {
 +
var menu1 = new menubar('nav', false);
 +
}); // end ready()
 +
 
 +
//
 +
// Function menubar() is the constructor of a menu widget
 +
// The widget will bind to the ul passed to it.
 +
//
 +
// @param(id string) id is the HTML id of the ul to bind to
 +
//
 +
// @param(vmenu boolean) vmenu is true if menu is vertical; false if horizontal
 +
//
 +
// @return N/A
 +
//
 +
function menubar(id, vmenu) {
 +
 
 +
// define widget properties
 +
this.$id = $('#' + id);
 +
 
 +
this.$rootItems = this.$id.children('li'); // jQuerry array of all root-level menu items
 +
 
 +
this.$items = this.$id.find('.menu-item').not('.separator'); // jQuery array of menu items
 +
 +
this.$parents = this.$id.find('.menu-parent'); // jQuery array of menu items
 +
 
 +
this.$allItems = this.$parents.add(this.$items); // jQuery array of all menu items
 +
 +
console.log('All items:');
 +
console.log(this.$allItems);
 +
console.log('Parents: ');
 +
console.log(this.$parents);
 +
 +
 +
this.$activeItem = null; // jQuery object of the menu item with focus
 +
 
 +
this.vmenu = vmenu;
 +
this.bChildOpen = false; // true if child menu is open
 +
 
 +
this.keys = {
 +
tab:    9,
 +
enter:  13,
 +
esc:    27,
 +
space:  32,
 +
left:  37,
 +
up:    38,
 +
right:  39,
 +
down:  40
 +
};
 +
 
 +
// bind event handlers
 +
this.bindHandlers();
 +
 
 +
// associate the menu with the textArea it controls
 +
// this.textarea = new textArea(this.$id.attr('aria-controls'));
 +
};
 +
 
 +
//
 +
// Function bindHandlers() is a member function to bind event handlers for the widget.
 +
//
 +
// @return N/A
 +
//
 +
menubar.prototype.bindHandlers = function() {
 +
 
 +
var thisObj = this;
 +
 
 +
///////// bind mouse event handlers //////////
 +
 
 +
// bind a handler for the menu items
 +
this.$items.mouseenter(function(e) {
 +
$(this).addClass('menu-hover');
 +
return true;
 +
});
 +
 
 +
// bind a mouseout handler for the menu items
 +
this.$items.mouseout(function(e) {
 +
$(this).removeClass('menu-hover');
 +
return true;
 +
});
 +
 
 +
// bind a mouseenter handler for the menu parents
 +
this.$parents.mouseenter(function(e) {
 +
return thisObj.handleMouseEnter($(this), e);
 +
});
 +
 
 +
// bind a mouseleave handler
 +
this.$parents.mouseleave(function(e) {
 +
return thisObj.handleMouseLeave($(this), e);
 +
});
 +
 
 +
// bind a click handler
 +
this.$allItems.click(function(e) {
 +
return thisObj.handleClick($(this), e);
 +
});
 +
 
 +
//////////// bind key event handlers //////////////////
 +
 
 +
// bind a keydown handler
 +
this.$allItems.keydown(function(e) {
 +
return thisObj.handleKeyDown($(this), e);
 +
});
 +
 
 +
// bind a keypress handler
 +
this.$allItems.keypress(function(e) {
 +
return thisObj.handleKeyPress($(this), e);
 +
});
 +
 
 +
// bind a focus handler
 +
this.$allItems.focus(function(e) {
 +
return thisObj.handleFocus($(this), e);
 +
});
 +
 
 +
// bind a blur handler
 +
this.$allItems.blur(function(e) {
 +
return thisObj.handleBlur($(this), e);
 +
});
 +
 
 +
// bind a document click handler
 +
$(document).click(function(e) {
 +
return thisObj.handleDocumentClick(e);
 +
});
 +
 
 +
} // end bindHandlers()
 +
 
 +
//
 +
// Function handleMouseEnter() is a member function to process mouseover
 +
// events for the top menus.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false;
 +
//
 +
menubar.prototype.handleMouseEnter = function($item, e) {
 +
 
 +
// add hover style
 +
$item.addClass('menu-hover');
 +
 
 +
// expand the first level submenu
 +
if ($item.attr('aria-haspopup') == 'true') {
 +
$item.children('ul').show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
}
 +
//e.stopPropagation();
 +
return true;
 +
 
 +
} // end handleMouseEnter()
 +
 
 +
//
 +
// Function handleMouseOut() is a member function to process mouseout
 +
// events for the top menus.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false;
 +
//
 +
menubar.prototype.handleMouseOut = function($item, e) {
 +
 
 +
// Remover hover styles
 +
$item.removeClass('menu-hover');
 +
 
 +
//e.stopPropagation();
 +
return true;
 +
 
 +
} // end handleMouseOut()
 +
 
 +
//
 +
// Function handleMouseLeave() is a member function to process mouseout
 +
// events for the top menus.
 +
//
 +
// @param($menu object) $menu is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false;
 +
//
 +
menubar.prototype.handleMouseLeave = function($menu, e) {
 +
 
 +
console.log('mouseLeave');
 +
 
 +
var $active = $menu.find('.menu-focus'); //???
 +
 
 +
$active = $active.add($menu.find('.menu-focus'));
 +
 
 +
// Remove hover style
 +
$menu.removeClass('menu-hover');
 +
 
 +
// if any item in the child menu has focus, move focus to the root item
 +
if ($active.length > 0) {
 +
 
 +
this.bChildOpen = false;
 +
 
 +
// remove the focus style from the active item
 +
$active.removeClass('menu-focus');
 +
 
 +
// store the active item
 +
this.$activeItem = $menu;
 +
 
 +
// cannot hide items with focus -- move focus to root item
 +
$menu.focus();
 +
}
 +
 
 +
// hide the child menu
 +
$menu.children('ul').hide().attr('aria-hidden', 'true');
 +
 
 +
//e.stopPropagation();
 +
return true;
 +
 
 +
} // end handleMouseLeave()
 +
 
 +
//
 +
// Function handleClick() is a member function to process click events
 +
// for the top menus.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false;
 +
//
 +
menubar.prototype.handleClick = function($item, e) {
 +
 
 +
var $parentUL = $item.parent();
 +
 
 +
if ($parentUL.is('.root-level')) {
 +
// open the child menu if it is closed
 +
$item.children('ul').first().show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
}
 +
else {
 +
// remove hover and focus styling
 +
this.$allItems.removeClass('menu-hover menu-focus');
 +
 
 +
// close the menu
 +
this.$id.find('ul').not('.root-level').hide().attr('aria-hidden','true');
 +
}
 +
 
 +
// if menu item triggers some behavior other than going to a link,
 +
// would stop propagation and return false
 +
// e.stopPropagation();
 +
// return false;
 +
return true;
 +
 
 +
} // end handleClick()
 +
 
 +
//
 +
// Function handleFocus() is a member function to process focus events
 +
// for the menu.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns true;
 +
//
 +
menubar.prototype.handleFocus = function($item, e) {
 +
 
 +
// if activeItem is null, we are getting focus from outside the menu. Store
 +
// the item that triggered the event
 +
if (this.$activeItem == null) {
 +
this.$activeItem = $item;
 +
}
 +
else if ($item[0] != this.$activeItem[0]) {
 +
return true;
 +
}
 +
 
 +
// get the set of jquery objects for all the parent items of the active item
 +
var $parentItems = this.$activeItem.parentsUntil('div').filter('li');
 +
 
 +
// remove focus styling from all other menu items
 +
this.$allItems.removeClass('menu-focus');
 +
 
 +
// add styling to the active item
 +
this.$activeItem.addClass('menu-focus');
 +
/*
 +
if (this.$activeItem.hasClass('menu-parent')) {
 +
// for parent items, add .menu-focus directly to the list item
 +
this.$activeItem.addClass('menu-focus');
 +
}
 +
else {
 +
// for sub-menu items, add .menu-focus to the anchor
 +
this.$activeItem.find('a').addClass('menu-focus');
 +
}
 +
*/
 +
// add styling to all parent items.
 +
$parentItems.addClass('menu-focus');
 +
 
 +
if (this.vmenu == true) {
 +
// if the bChildOpen is true, open the active item's child menu (if applicable)
 +
if (this.bChildOpen == true) {
 +
 
 +
var $itemUL = $item.parent();
 +
 
 +
// if the itemUL is a root-level menu and item is a parent item,
 +
// show the child menu.
 +
if ($itemUL.is('.root-level') && ($item.attr('aria-haspopup') == 'true')) {
 +
$item.children('ul').show().attr('aria-hidden', 'false');
 +
}
 +
}
 +
else {
 +
this.vmenu = false;
 +
}
 +
}
 +
 
 +
return true;
 +
 
 +
} // end handleFocus()
 +
 
 +
//
 +
// Function handleBlur() is a member function to process blur events
 +
// for the menu.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns true;
 +
//
 +
menubar.prototype.handleBlur = function($item, e) {
 +
 
 +
// $item.find('a').removeClass('menu-focus');
 +
$item.removeClass('menu-focus');
 +
 
 +
return true;
 +
 
 +
} // end handleBlur()
 +
 
 +
//
 +
// Function handleKeyDown() is a member function to process keydown events
 +
// for the menus.
 +
//
 +
// @param($item object) $item is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false if consuming; true if propagating
 +
//
 +
menubar.prototype.handleKeyDown = function($item, e) {
 +
 
 +
if (e.altKey || e.ctrlKey) {
 +
// Modifier key pressed: Do not process
 +
return true;
 +
}
 +
 
 +
switch(e.keyCode) {
 +
case this.keys.tab: {
 +
 
 +
// hide all menu items and update their aria attributes
 +
this.$id.find('ul').hide().attr('aria-hidden', 'true');
 +
 
 +
// remove focus styling from all menu items
 +
this.$allItems.removeClass('menu-focus');
 +
 
 +
this.$activeItem = null;
 +
this.bChildOpen == false;
 +
 
 +
break;
 +
}
 +
case this.keys.esc: {
 +
 
 +
var $itemUL = $item.parent();
 +
 
 +
if ($itemUL.is('.root-level')) {
 +
// hide the child menu and update the aria attributes
 +
$item.children('ul').first().hide().attr('aria-hidden', 'true');
 +
}
 +
else {
 +
 
 +
// move up one level
 +
this.$activeItem = $itemUL.parent();
 +
 
 +
// reset the childOpen flag
 +
this.bChildOpen = false;
 +
 
 +
// set focus on the new item
 +
this.$activeItem.focus();
 +
 
 +
// hide the active menu and update the aria attributes
 +
$itemUL.hide().attr('aria-hidden', 'true');
 +
}
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
case this.keys.enter:
 +
case this.keys.space: {
 +
 
 +
if (!($item.hasClass('menu-parent'))) {
 +
// user pressed enter or space on a dropdown menu item,
 +
// not an item on the menu bar
 +
// get the target href and go there
 +
window.location = $item.find('a').attr('href');
 +
return false;
 +
}
 +
 
 +
var $parentUL = $item.parent();
 +
 
 +
if ($parentUL.is('.root-level')) {
 +
// open the child menu if it is closed
 +
$item.children('ul').first().show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
}
 +
else {
 +
// remove hover styling
 +
this.$allItems.removeClass('menu-hover');
 +
this.$allItems.removeClass('menu-focus');
 +
 
 +
// close the menu
 +
this.$id.find('ul').not('.root-level').hide().attr('aria-hidden','true');
 +
 
 +
// clear the active item
 +
this.$activeItem = null;
 +
 
 +
}
 +
e.stopPropagation();
 +
return false;
 +
}
 +
 
 +
case this.keys.left: {
 +
 
 +
if (this.vmenu == true && $itemUL.is('.root-level')) {
 +
// If this is a vertical menu and the root-level is active, move
 +
// to the previous item in the menu
 +
this.$activeItem = this.moveUp($item);
 +
}
 +
else {
 +
this.$activeItem = this.moveToPrevious($item);
 +
}
 +
 
 +
this.$activeItem.focus();
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
case this.keys.right: {
 +
 
 +
if (this.vmenu == true && $itemUL.is('.root-level')) {
 +
// If this is a vertical menu and the root-level is active, move
 +
// to the next item in the menu
 +
this.$activeItem = this.moveDown($item);
 +
}
 +
else {
 +
this.$activeItem = this.moveToNext($item);
 +
}
 +
 
 +
this.$activeItem.focus();
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
case this.keys.up: {
 +
 
 +
if (this.vmenu == true && $itemUL.is('.root-level')) {
 +
// If this is a vertical menu and the root-level is active, move
 +
// to the previous root-level menu
 +
this.$activeItem = this.moveToPrevious($item);
 +
}
 +
else {
 +
this.$activeItem = this.moveUp($item);
 +
}
 +
 
 +
this.$activeItem.focus();
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
case this.keys.down: {
 +
 
 +
if (this.vmenu == true && $itemUL.is('.root-level')) {
 +
// If this is a vertical menu and the root-level is active, move
 +
// to the next root-level menu
 +
this.$activeItem = this.moveToNext($item);
 +
}
 +
else {
 +
this.$activeItem = this.moveDown($item);
 +
}
 +
 
 +
this.$activeItem.focus();
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
} // end switch
 +
return true;
 +
 
 +
} // end handleKeyDown()
 +
 
 +
//
 +
// Function moveToNext() is a member function to move to the next menu level.
 +
// This will be either the next root-level menu or the child of a menu parent. If
 +
// at the root level and the active item is the last in the menu, this function will loop
 +
// to the first menu item.
 +
//
 +
// If the menu is a horizontal menu, the first child element of the newly selected menu will
 +
// be selected
 +
//
 +
// @param($item object) $item is the active menu item
 +
//
 +
// @return (object) Returns the item to move to. Returns $item is no move is possible
 +
//
 +
menubar.prototype.moveToNext = function($item) {
 +
 
 +
var $itemUL = $item.parent(); // $item's containing menu
 +
var $menuItems = $itemUL.children('li'); // the items in the currently active menu
 +
var menuNum = $menuItems.length; // the number of items in the active menu
 +
var menuIndex = $menuItems.index($item); // the items index in its menu
 +
var $newItem = null;
 +
var $newItemUL = null;
 +
 
 +
if ($itemUL.is('.root-level')) {
 +
// this is the root level move to next sibling. This will require closing
 +
// the current child menu and opening the new one.
 +
 
 +
if (menuIndex < menuNum-1) { // not the last root menu
 +
$newItem = $item.next();
 +
}
 +
else { // wrap to first item
 +
$newItem = $menuItems.first();
 +
}
 +
 
 +
// close the current child menu (if applicable)
 +
if ($item.attr('aria-haspopup') == 'true') {
 +
 
 +
var $childMenu = $item.children('ul').first();
 +
 
 +
if ($childMenu.attr('aria-hidden') == 'false') {
 +
// hide the child and update aria attributes accordingly
 +
$childMenu.hide().attr('aria-hidden', 'true');
 +
this.bChildOpen = true;
 +
}
 +
}
 +
 
 +
// remove the focus styling from the current menu
 +
$item.removeClass('menu-focus');
 +
 
 +
// open the new child menu (if applicable)
 +
if (($newItem.attr('aria-haspopup') == 'true') && (this.bChildOpen == true)) {
 +
 
 +
var $childMenu = $newItem.children('ul').first();
 +
 
 +
// open the child and update aria attributes accordingly
 +
$childMenu.show().attr('aria-hidden', 'false');
 +
 
 +
if (!this.vmenu) {
 +
// select the first item in the child menu
 +
$newItem = $childMenu.children('li').first();
 +
}
 +
 
 +
}
 +
}
 +
else {
 +
// this is not the root level. If there is a child menu to be moved into, do that;
 +
// otherwise, move to the next root-level menu if there is one
 +
if ($item.attr('aria-haspopup') == 'true') {
 +
       
 +
var $childMenu = $item.children('ul').first();
 +
 
 +
$newItem = $childMenu.children('li').first();
 +
 
 +
// show the child menu and update its aria attributes
 +
$childMenu.show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
}
 +
else {
 +
// at deepest level, move to the next root-level menu
 +
 
 +
if (this.vmenu == true) {
 +
// do nothing
 +
return $item;
 +
}
 +
 
 +
var $parentMenus = null;
 +
var $rootItem = null;
 +
 
 +
// get list of all parent menus for item, up to the root level
 +
$parentMenus = $item.parentsUntil('div').filter('ul').not('.root-level');
 +
 
 +
// hide the current menu and update its aria attributes accordingly
 +
$parentMenus.hide().attr('aria-hidden', 'true');
 +
 
 +
// remove the focus styling from the active menu
 +
$parentMenus.find('li').removeClass('menu-focus');
 +
$parentMenus.last().parent().removeClass('menu-focus');
 +
 
 +
$rootItem = $parentMenus.last().parent(); // the containing root for the menu
 +
 
 +
menuIndex = this.$rootItems.index($rootItem);
 +
 
 +
// if this is not the last root menu item, move to the next one
 +
if (menuIndex < this.$rootItems.length-1) {
 +
$newItem = $rootItem.next();
 +
}
 +
else { // loop
 +
$newItem = this.$rootItems.first();
 +
}
 +
 
 +
if ($newItem.attr('aria-haspopup') == 'true') {
 +
var $childMenu = $newItem.children('ul').first();
 +
 
 +
$newItem = $childMenu.children('li').first();
 +
 
 +
// show the child menu and update it's aria attributes
 +
$childMenu.show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
}
 +
}
 +
}
 +
 
 +
return $newItem;
 +
}
 +
 
 +
//
 +
// Function moveToPrevious() is a member function to move to the previous menu level.
 +
// This will be either the previous root-level menu or the child of a menu parent. If
 +
// at the root level and the active item is the first in the menu, this function will loop
 +
// to the last menu item.
 +
//
 +
// If the menu is a horizontal menu, the first child element of the newly selected menu will
 +
// be selected
 +
//
 +
// @param($item object) $item is the active menu item
 +
//
 +
// @return (object) Returns the item to move to. Returns $item is no move is possible
 +
//
 +
menubar.prototype.moveToPrevious = function($item) {
 +
 
 +
var $itemUL = $item.parent(); // $item's containing menu
 +
var $menuItems = $itemUL.children('li'); // the items in the currently active menu
 +
var menuNum = $menuItems.length; // the number of items in the active menu
 +
var menuIndex = $menuItems.index($item); // the items index in its menu
 +
var $newItem = null;
 +
var $newItemUL = null;
 +
 
 +
if ($itemUL.is('.root-level')) {
 +
// this is the root level move to previous sibling. This will require closing
 +
// the current child menu and opening the new one.
 +
 
 +
if (menuIndex > 0) { // not the first root menu
 +
$newItem = $item.prev();
 +
}
 +
else { // wrap to last item
 +
$newItem = $menuItems.last();
 +
}
 +
 
 +
// close the current child menu (if applicable)
 +
if ($item.attr('aria-haspopup') == 'true') {
 +
 
 +
var $childMenu = $item.children('ul').first();
 +
 
 +
if ($childMenu.attr('aria-hidden') == 'false') {
 +
// hide the child and update aria attributes accordingly
 +
$childMenu.hide().attr('aria-hidden', 'true');
 +
this.bChildOpen = true;
 +
}
 +
}
 +
 
 +
// remove the focus styling from the current menu
 +
$item.removeClass('menu-focus');
 +
 
 +
// open the new child menu (if applicable)
 +
if (($newItem.attr('aria-haspopup') == 'true') && this.bChildOpen == true) {
 +
 
 +
var $childMenu = $newItem.children('ul').first();
 +
 
 +
// open the child and update aria attributes accordingly
 +
$childMenu.show().attr('aria-hidden', 'false');
 +
 
 +
if (!this.vmenu) {
 +
// select the first item in the child menu
 +
$newItem = $childMenu.children('li').first();
 +
}
 +
}
 +
}
 +
else {
 +
// this is not the root level. If there is a parent menu that is not the
 +
// root menu, move up one level; otherwise, move to first item of the previous
 +
// root menu.
 +
 
 +
var $parentLI = $itemUL.parent();
 +
var $parentUL = $parentLI.parent();
 +
 
 +
var $parentMenus = null;
 +
var $rootItem = null;
 +
 
 +
// if this is a vertical menu or is not the first child menu
 +
// of the root-level menu, move up one level.
 +
if (this.vmenu == true || !$parentUL.is('.root-level')) {
 +
 
 +
$newItem = $itemUL.parent();
 +
 
 +
// hide the active menu and update aria-hidden
 +
$itemUL.hide().attr('aria-hidden', 'true');
 +
 
 +
// remove the focus highlight from the $item
 +
$item.removeClass('menu-focus');
 +
 
 +
if (this.vmenu == true) {
 +
// set a flag so the focus handler does't reopen the menu
 +
this.bChildOpen = false;
 +
}
 +
}
 +
else { // move to previous root-level menu
 +
 
 +
// hide the current menu and update the aria attributes accordingly
 +
$itemUL.hide().attr('aria-hidden', 'true');
 +
 
 +
// remove the focus styling from the active menu
 +
$item.removeClass('menu-focus');
 +
$parentLI.removeClass('menu-focus');
 +
 
 +
menuIndex = this.$rootItems.index($parentLI);
 +
 
 +
if (menuIndex > 0) {
 +
// move to the previous root-level menu
 +
$newItem = $parentLI.prev();
 +
}
 +
else { // loop to last root-level menu
 +
$newItem = this.$rootItems.last();
 +
}
 +
 
 +
// add the focus styling to the new menu
 +
$newItem.addClass('menu-focus');
 +
 
 +
if ($newItem.attr('aria-haspopup') == 'true') {
 +
var $childMenu = $newItem.children('ul').first();
 +
 
 +
// show the child menu and update it's aria attributes
 +
$childMenu.show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
 
 +
$newItem = $childMenu.children('li').first();
 +
}
 +
}
 +
}
 +
return $newItem;
 +
}
 +
 
 +
//
 +
// Function moveDown() is a member function to select the next item in a menu.
 +
// If the active item is the last in the menu, this function will loop to the
 +
// first menu item.
 +
//
 +
// @param($item object) $item is the active menu item
 +
//
 +
// @param(startChr char) [optional] startChr is the character to attempt to match against the beginning of the
 +
// menu item titles. If found, focus moves to the next menu item beginning with that character.
 +
//
 +
// @return (object) Returns the item to move to. Returns $item is no move is possible
 +
//
 +
menubar.prototype.moveDown = function($item, startChr) {
 +
 
 +
var $itemUL = $item.parent(); // $item's containing menu
 +
var $menuItems = $itemUL.children('li').not('.separator'); // the items in the currently active menu
 +
var menuNum = $menuItems.length; // the number of items in the active menu
 +
var menuIndex = $menuItems.index($item); // the items index in its menu
 +
var $newItem = null;
 +
var $newItemUL = null;
 +
 
 +
if ($itemUL.is('.root-level')) { // this is the root level menu
 +
 
 +
if ($item.attr('aria-haspopup') != 'true') {
 +
// No child menu to move to
 +
return $item;
 +
}
 +
 
 +
// Move to the first item in the child menu
 +
$newItemUL = $item.children('ul').first();
 +
$newItem = $newItemUL.children('li').first();
 +
 
 +
// make sure the child menu is visible
 +
$newItemUL.show().attr('aria-hidden', 'false');
 +
this.bChildOpen = true;
 +
 
 +
return $newItem;
 +
}
 +
 
 +
// if $item is not the last item in its menu, move to the next item. If startChr is specified, move
 +
// to the next item with a title that begins with that character.
 +
//
 +
if (startChr) {
 +
 +
var bMatch = false;
 +
var curNdx = menuIndex+1;
 +
 
 +
// check if the active item was the last one on the list
 +
if (curNdx == menuNum) {
 +
curNdx = 0;
 +
}
 +
 
 +
// Iterate through the menu items (starting from the current item and wrapping) until a match is found
 +
// or the loop returns to the current menu item
 +
while (curNdx != menuIndex)  {
 +
 
 +
// Use the first of the two following lines if menu does not contain anchor tags.
 +
// Otherwise use the second
 +
// var titleChr = $menuItems.eq(curNdx).html().charAt(0);
 +
var titleChr = $menuItems.eq(curNdx).find('a').html().charAt(0);
 +
 +
if (titleChr.toLowerCase() == startChr) {
 +
bMatch = true;
 +
break;
 +
}
 +
 
 +
curNdx = curNdx+1;
 +
 
 +
if (curNdx == menuNum) {
 +
// reached the end of the list, start again at the beginning
 +
curNdx = 0;
 +
}
 +
}
 +
 
 +
if (bMatch == true) {
 +
$newItem = $menuItems.eq(curNdx);
 +
 
 +
// remove the focus styling from the current item
 +
$item.removeClass('menu-focus');
 +
 
 +
return $newItem
 +
}
 +
else {
 +
return $item;
 +
}
 +
}
 +
else {
 +
if (menuIndex < menuNum-1) {
 +
$newItem = $menuItems.eq(menuIndex+1);
 +
}
 +
else {
 +
$newItem = $menuItems.first();
 +
}
 +
}
 +
 
 +
// remove the focus styling from the current item
 +
$item.removeClass('menu-focus');
 +
 
 +
return $newItem;
 +
}
 +
 
 +
//
 +
// Function moveUp() is a member function to select the previous item in a menu.
 +
// If the active item is the first in the menu, this function will loop to the
 +
// last menu item.
 +
//
 +
// @param($item object) $item is the active menu item
 +
//
 +
// @return (object) Returns the item to move to. Returns $item is no move is possible
 +
//
 +
menubar.prototype.moveUp = function($item) {
 +
 
 +
var $itemUL = $item.parent(); // $item's containing menu
 +
var $menuItems = $itemUL.children('li').not('.separator'); // the items in the currently active menu
 +
var menuNum = $menuItems.length; // the number of items in the active menu
 +
var menuIndex = $menuItems.index($item); // the items index in its menu
 +
var $newItem = null;
 +
var $newItemUL = null;
 +
 
 +
if ($itemUL.is('.root-level')) { // this is the root level menu
 +
 
 +
// nothing to do
 +
return $item;
 +
}
 +
 
 +
// if $item is not the first item in its menu, move to the previous item
 +
if (menuIndex > 0) {
 +
 
 +
$newItem = $menuItems.eq(menuIndex-1);
 +
}
 +
else {
 +
// loop to top of menu
 +
$newItem = $menuItems.last();
 +
}
 +
 
 +
// remove the focus styling from the current item
 +
$item.removeClass('menu-focus');
 +
 
 +
return $newItem;
 +
}
 +
 
 +
//
 +
// Function handleKeyPress() is a member function to process keydown events
 +
// for the menus.
 +
//
 +
// The Opera browser performs some window commands from the keypress event,
 +
// not keydown like Firefox, Safari, and IE. This event handler consumes
 +
// keypresses for relevant keys so that Opera behaves when the user is
 +
// manipulating the menu with the keyboard.
 +
//
 +
// @param($item object) $menu is the jquery object of the item firing the event
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns false if consuming; true if propagating
 +
//
 +
menubar.prototype.handleKeyPress = function($item, e) {
 +
 
 +
if (e.altKey || e.ctrlKey || e.shiftKey) {
 +
// Modifier key pressed: Do not process
 +
return true;
 +
}
 +
 
 +
switch(e.keyCode) {
 +
case this.keys.tab: {
 +
return true;
 +
}
 +
case this.keys.esc:
 +
case this.keys.enter:
 +
case this.keys.space:
 +
case this.keys.up:
 +
case this.keys.down:
 +
case this.keys.left:
 +
case this.keys.right: {
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
default : {
 +
var chr = String.fromCharCode(e.which);
 +
 
 +
this.$activeItem = this.moveDown($item, chr);
 +
this.$activeItem.focus();
 +
 
 +
e.stopPropagation();
 +
return false;
 +
}
 +
} // end switch
 +
return true;
 +
 
 +
} // end handleKeyPress()
 +
 
 +
//
 +
// Function handleDocumentClick() is a member function to process click events on the document. Needed
 +
// to close an open menu if a user clicks outside the menu
 +
//
 +
// @param(e object) e is the associated event object
 +
//
 +
// @return(boolean) Returns true;
 +
//
 +
menubar.prototype.handleDocumentClick = function(e) {
 +
 
 +
// get a list of all child menus
 +
var $childMenus = this.$id.find('ul').not('.root-level');
 +
 
 +
// hide the child menus
 +
$childMenus.hide().attr('aria-hidden', 'true');
 +
 
 +
this.$allItems.removeClass('menu-focus');
 +
 
 +
this.$activeItem = null;
 +
 
 +
// allow the event to propagate
 +
return true;
 +
 
 +
} // end handleDocumentClick()
 +
</script>
 
</head>
 
</head>
 
<body>
 
<body>
<div role="menubar">  
+
<p>This is an example of an ARIA menubar.</p>
<!--
+
<div role="navigation" aria-label="Main menu" tabindex="-1">
File menu: File menuitem has an aria-haspopup attribute set to 'true'.
+
<ul id="nav" class="menubar root-level" role="menubar">
That popup div follows immediately below.
+
<li class="menu-parent" role="menuitem" tabindex="0" aria-haspopup="true" title="About Menu">
-->
+
About
<div role="menuitem" aria-haspopup="true" id="fileMenu">File</div>
+
<ul id="about" role="menu" class="menu" aria-hidden="true">
<div role="menu" aria-labelledby="fileMenu">
+
<li role="menuitem" class="menu-item" tabindex="-1">
<div role="menuitem">Open</div>
+
<a href="https://www.ssbbartgroup.com/about_us/index.php">Company Overview</a></li>
<div role="menuitem">Save</div>
+
<li role="menuitem" class="menu-item" tabindex="-1">
<div role="menuitem">Save as ...</div>
+
<a href="https://www.ssbbartgroup.com/about_us/executive_team.php">Executive Team</a></li>
</div>
+
<li role="menuitem" class="menu-item" tabindex="-1">
+
<a href="https://www.ssbbartgroup.com/about_us/the_ssb_advantage.php">The SSB Advantage</a></li>
<!--
+
<li role="menuitem" class="menu-item" tabindex="-1">
View menu:
+
<a href="https://www.ssbbartgroup.com/about_us/clients.php">Sample Clients</a></li>
-->
+
</ul>
<div role="menuitem" aria-haspopup="true" id="viewMenu">View</div>
+
</li>
<div role="menu" aria-labelledby="viewMenu">
+
<li class="menu-parent" role="menuitem" tabindex="0" aria-haspopup="true" title="Services Menu">
<div role="menuitem">Normal</div>
+
Services
<div role="menuitem">Outline</div>
+
<ul role="menu" class="menu" aria-hidden="true">
+
<li role="menuitem" class="menu-item" tabindex="-1">
<!--
+
<a href="https://www.ssbbartgroup.com/services/accessibility_staffing.php">Staffing</a></li>
The View's Zoom menuitem has aria-haspopup='true' as it leads to a submenu.
+
<li role="menuitem" class="menu-item" tabindex="-1">
-->
+
<a href="https://www.ssbbartgroup.com/services/audit/index.php">Audits</a></li>
<div role="menuitem" aria-haspopup="true" id="zoomSubMenu">Zoom</div>
+
<li role="menuitem" class="menu-item" tabindex="-1">
<div role="menu" aria-labelledby="zoomSubMenu">
+
<a href="https://www.ssbbartgroup.com/services/implementation_support/index.php">Implementation Support</a></li>
<div role="menuitem">50%</div>
+
<li role="menuitem" class="menu-item" tabindex="-1">
<div role="menuitem">75%</div>
+
<a href="https://www.ssbbartgroup.com/services/accessibility_policy_development.php">Accessibility Policy Development</a></li>
<div role="menuitem">100%</div>
+
</ul>
<div role="menuitem">150%</div>
+
</li>
<div role="menuitem">200%</div>
+
<li class="menu-parent" role="menuitem" tabindex="0" aria-haspopup="true" title="AMP Menu">
</div>
+
AMP
</div>
+
<ul role="menu" class="menu" aria-hidden="true">
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://amp.ssbbartgroup.com/register/">Free Trial</a></li>
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://www.ssbbartgroup.com/amp/the_amp_advantage.php">The AMP Advantage</a></li>
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://www.ssbbartgroup.com/amp/supportedstandards.php">Supported Standards</a></li>
 +
</ul>
 +
</li>
 +
<li class="menu-parent" role="menuitem" tabindex="0" aria-haspopup="true" title="Training Menu">
 +
Training
 +
<ul role="menu" class="menu" aria-hidden="true">
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://www.ssbbartgroup.com/training/index.php">Overview</a></li>
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://www.ssbbartgroup.com/training/course_list.php">Course List</a></li>
 +
<li role="menuitem" class="menu-item" tabindex="-1">
 +
<a href="https://www.ssbbartgroup.com/training/delivery_options.php">Delivery Options</a></li>
 +
</ul>
 +
</li>
 +
</ul>
 
</div>
 
</div>
 +
<p>To exit the menu, <a href="#">press tab again</a> and you should land safely in this paragraph.</p>
 
</body>
 
</body>
 
</html>
 
</html>
 
[[Category:ARIA]]
 
[[Category:ARIA]]

Revision as of 12:29, 10 April 2014

ARIA Menu Example

This is an example of an ARIA menubar.

To exit the menu, press tab again and you should land safely in this paragraph.