Difference between revisions of "ARIA Menu"

From Level Access Web Labs
Jump to navigation Jump to search
Line 3: Line 3:
 
<title>ARIA Menu Example</title>
 
<title>ARIA Menu Example</title>
 
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script src="http://test.cita.illinois.edu/aria/js/constants.js" type="text/javascript"></script>
 
    <script src="http://test.cita.illinois.edu/aria/js/jquery.js" type="text/javascript"></script>
 
<style type="text/css">
 
/* CSS Document */
 
#st1 {
 
  padding: .5em;
 
  border: 1px solid black;
 
  height: 400px;
 
  width: 70%;
 
  font-size: medium;
 
  font-family: sans-serif;
 
}
 
ul.menubar {
 
  margin: 0;
 
  padding: 0 .25em;
 
  list-style: none;
 
  border: 1px solid black;
 
  height: 1.85em;
 
  width: 20em;
 
  background: #ccc;
 
}
 
ul.menubar li {
 
  float: left;
 
  display: inline; position: relative;
 
  margin: 0;
 
  padding: .25em .35em;
 
  height: 1.25em;
 
}
 
ul.menu {
 
  position: absolute;
 
  left: 0;
 
  top: 1.75em;
 
  display: none;
 
  list-style: none;
 
  margin: 0;
 
  padding: 0;
 
  font-weight: normal;
 
  border: 1px solid black;
 
  background-color: #ccc;
 
  width: auto;
 
}
 
ul.menu li {
 
  float: none;
 
  display: block;
 
  white-space: nowrap;
 
  padding: 2px 1em;
 
  color: black;
 
}
 
 
li.separator {
 
  margin: 0;
 
  margin-bottom: 4px !important;
 
  height: 2px !important;
 
  border-bottom: 1px solid black;
 
}
 
 
li.checked {
 
  font-weight: bold;
 
  background-image: url('images/dot.png');
 
  background-repeat: no-repeat;
 
  background-position: 5px 10px;
 
}
 
 
.hover {
 
  background-color: #700 !important;
 
  color: white !important;
 
}
 
 
.hover-checked {
 
  background-color: #700 !important;
 
  color: white !important;
 
  background-image: url('images/dot-selected.png') !important;
 
}
 
 
.focus {
 
  background-color: black;
 
  font-weight: bold;
 
  color: white !important;
 
}
 
 
li.focus-checked {
 
  background-color: black;
 
  font-weight: bold;
 
  color: white !important;
 
  background-image: url('images/dot-selected.png') !important;
 
}
 
 
/*
 
* Text area styles
 
*/
 
.italic {
 
  font-style: italic;
 
}
 
.bold {
 
  font-weight: bold;
 
}
 
.underline {
 
  text-decoration: underline;
 
}
 
  </style>
 
<script type="text/javascript">
 
var g_closetimer = undefined; // document timer object for menu close
 
var menuApp = undefined;
 
 
$(document).ready(function(event) {
 
 
  menuApp = new menubar("mb1");
 
 
 
}); // end document ready
 
 
//
 
// Function keyCodes() is a object which defines keycodes for the application
 
//
 
// @return N/A
 
//
 
function keyCodes() {
 
 
  // key code definitions
 
  this.tab  = 9;
 
  this.enter = 13;
 
  this.esc  = 27;
 
  this.space = 32;
 
  this.left  = 37;
 
  this.up    = 38;
 
  this.right = 39;
 
  this.down  = 40;
 
 
} // end keyCodes();
 
 
 
/////////////// begin textArea widget definition ////////////////////////
 
 
//
 
// Function textArea() is the constructor of a widget to manipulate the
 
// properties of a text area.
 
//
 
// @param(id string) id is the HTML id of the text area to bind to
 
//
 
// @return N/A
 
//
 
function textArea(id) {
 
 
  // define widget properties
 
  this.$id = $('#' + id);
 
 
  this.fontSizes = new Array('x-small', 'small', 'medium', 'large', 'x-large');
 
  this.sizeNdx = 2; // index of current size setting
 
 
} // end textArea() constructor
 
 
//
 
// setFont() is a member function to set the font family of the text area.
 
//
 
// @param ( fontFamily string ) name of family to set
 
//
 
// @return N/A
 
//
 
textArea.prototype.setFont = function(fontFamily) {
 
 
  this.$id.css('font-family', fontFamily);
 
 
} // end setFont()
 
 
//
 
// setStyle() is a member function to set the font style of the text area.
 
//
 
// @param ( style string ) name of style to set
 
//
 
// @return N/A
 
//
 
textArea.prototype.setStyle = function(style) {
 
 
  this.$id.toggleClass(style);
 
 
} // end setStyle()
 
 
//
 
// setAlignment() is a member function to set the text alignment of the text area
 
//
 
// @param ( justification string ) name of style to set
 
//
 
// @return N/A
 
//
 
textArea.prototype.setAlignment = function(justification) {
 
 
  this.$id.css('text-align', justification);
 
 
} // end setAlignment()
 
 
//
 
// Function getSize() returns the size of text in the text area.
 
//
 
// @return N/A
 
//
 
textArea.prototype.getSize = function() {
 
 
  return this.fontSizes[this.sizeNdx];
 
 
} // end getSize()
 
 
//
 
// Function setSize() sets the size of text in the text area.
 
//
 
// @param ( size string ) size of text to set
 
//
 
// @return N/A
 
//
 
textArea.prototype.setSize = function(size) {
 
 
  switch (size) {
 
      case 'larger': {
 
          this.sizeNdx += 1;
 
          if (this.sizeNdx > 4) {
 
              this.sizeNdx = 4;
 
          }
 
          break;
 
      }
 
      case 'smaller': {
 
          this.sizeNdx -= 1;
 
          if (this.sizeNdx < 0) {
 
              this.sizeNdx = 0;
 
          }
 
          break;
 
      }
 
      case 'x-small': {
 
          this.sizeNdx = 0;
 
          break;
 
      }
 
      case 'small': {
 
          this.sizeNdx = 1;
 
          break;
 
      }
 
      case 'medium': {
 
          this.sizeNdx = 2;
 
          break;
 
      }
 
      case 'large': {
 
          this.sizeNdx = 3;
 
          break;
 
      }
 
      case 'x-large': {
 
          this.sizeNdx = 4;
 
          break;
 
      }
 
  } // end switch
 
 
  // set the new size
 
  this.$id.css('font-size', this.fontSizes[this.sizeNdx]);
 
 
} // end setSize();
 
 
/////////////// end textArea widget definition ////////////////////////
 
 
/////////////// begin menubar widget definition /////////////////////
 
 
//
 
// Function menubar() is the constructor of a menubar widget to manipulate a text area.
 
// The widget will bind to the ul passed to it.
 
//
 
// As written, this widget will only handle a single level of menu.
 
//
 
// @param(id string) id is the HTML id of the ul to bind to
 
//
 
// @return N/A
 
//
 
function menubar(id) {
 
 
  // define widget properties
 
  this.$id = $('#' + id);
 
  this.$menus = this.$id.children('li'); // jQuery array of top level menus
 
  this.$items = this.$menus.find('li').not('.separator'); // jQuery array of menu items
 
 
  this.keys = new keyCodes();
 
  this.timeout = 100; // number of milliseconds to delay before closing a menu
 
 
  this.$openMenu = undefined; // current open menu object
 
 
  // create the textArea widget
 
  this.textarea = new textArea(this.$id.attr('aria-controls'));
 
 
  this.itemFocus = false; // set to true when focus moves from a menu to a menu item (IE fix)
 
 
  // do initial styling, etc.
 
  this.init();
 
 
  // bind event handlers
 
  this.bindHandlers();
 
};
 
 
//
 
// Function init() is a member function to perform initial processing of the widget, such as
 
// applying initial styling to menu items.
 
//
 
// @return N/A
 
//
 
menubar.prototype.init = function() {
 
 
  // add the checked class to visually show checked items
 
  this.$items.filter('[aria-checked=true]').addClass('checked');
 
 
} // end init()
 
 
//
 
// Function closeMenu() is a member function to close an open menu and modify its
 
// aria attributes accordingly. If a menu item has focus, focus is set on the
 
// menu itself
 
//
 
// @param($id object) $id is the jquery object of the menu to close
 
//
 
// @parem(setFocus boolean) setFocus is true if focus should be set on the menu; false if not
 
// @return N/A
 
//
 
menubar.prototype.closeMenu = function($id, setFocus) {
 
 
  var $subMenu = $id.children('ul');
 
 
  // unbind the mouse delegate handlers. Prevents conflicts during menu close animations.
 
  $subMenu.undelegate('li', 'mouseover');
 
  $subMenu.undelegate('li', 'mouseout');
 
  $subMenu.undelegate('li', 'click');
 
 
  if ($id) {
 
    var $menu = $id.children('ul');
 
 
    // remove the focus class from the listitems in the menu
 
    $menu.find('li').removeClass('focus focus-checked');
 
   
 
    // Set aria-hidden to true and hide the subMenu
 
    $menu.attr('aria-hidden', 'true').hide();
 
 
    if (setFocus == true) {
 
      // need to return focus to the menu to prevent a non-visible
 
      // item having focus
 
      $id.focus();
 
    }
 
  }
 
 
  // clear openMenu
 
  this.$openMenu = undefined;
 
 
} // end closeMenu()
 
 
//
 
// Function showMenu() is a member function to open a menu and modify its
 
// aria attributes accordingly.
 
//
 
// @param($menu object) $menu is the jquery object of the menu to open
 
//
 
// @return N/A
 
//
 
menubar.prototype.showMenu = function($menu) {
 
 
  var $subMenu = $menu.children('ul');
 
  var thisObj = this;
 
 
  // Bind mouse event delegates. Binding event handlers to the menu
 
  // items only when the menu is shown helps prevent conflicts during
 
  // menu close animations. Using delegates is much faster than
 
  // binding to each menu item.
 
 
 
  // bind a mouseover delegate
 
  $subMenu.delegate('li', 'mouseover', function(e) {
 
    return thisObj.handleItemMouseOver($(this), e);
 
  });
 
 
  // bind a mouseout delegate
 
  $subMenu.delegate('li', 'mouseout', function(e) {
 
    return thisObj.handleItemMouseOut($(this), e);
 
  });
 
 
  // bind a click delegate
 
  $subMenu.delegate('li', 'click', function(e) {
 
    return thisObj.handleItemClick($(this), e);
 
  });
 
 
  // set aria-hidden to false and show the subMenu
 
  $subMenu.attr('aria-hidden', 'false').show();
 
 
  // bind the mouseover event delegate
 
  this.$openMenu = $menu;
 
 
} // end showMenu()
 
 
//
 
// Function findParentMenu() is a member function to find the parent menu
 
// of a menu item.
 
//
 
// @param($item object) $item is the jquery object of the item to find parent of
 
//
 
// @return(object) Returns the jQuery object of the parent menu;
 
//
 
menubar.prototype.findParentMenu = function($item) {
 
 
  var $menu = undefined;
 
 
 
  this.$menus.each(function() {
 
    if ($(this).find('li').index($item) != -1) {
 
 
      // this is the parent menu, store it
 
      $menu = $(this);
 
 
      // break out of the loop
 
      return false;
 
    }
 
  });
 
 
  return $menu;
 
 
} // end findParentMenu()
 
 
//
 
// Function bindHandlers() is a member function to bind event handlers for the widget.
 
//
 
// @return N/A
 
//
 
menubar.prototype.bindHandlers = function() {
 
 
  var thisObj = this;
 
 
  ///////// bind top level menu event handlers //////////
 
 
  // bind a mouseover handler
 
  this.$menus.mouseover(function(e) {
 
    return thisObj.handleMenuMouseOver($(this), e);
 
  });
 
 
  // bind a mouseout handler
 
  this.$menus.mouseout(function(e) {
 
    return thisObj.handleMenuMouseOut($(this), e);
 
  });
 
 
  // bind a click handler
 
  this.$menus.click(function(e) {
 
    return thisObj.handleMenuClick($(this), e);
 
  });
 
 
  // bind a keydown handler
 
  this.$menus.keydown(function(e) {
 
    return thisObj.handleMenuKeyDown($(this), e);
 
  });
 
 
  // bind a keypress handler
 
  this.$menus.keypress(function(e) {
 
    return thisObj.handleMenuKeyPress($(this), e);
 
  });
 
 
  // bind a focus handler
 
  this.$menus.focus(function(e) {
 
    return thisObj.handleMenuFocus($(this), e);
 
  });
 
 
  // bind a blur handler
 
  this.$menus.blur(function(e) {
 
    return thisObj.handleMenuBlur($(this), e);
 
  });
 
 
  ///////// bind submenu event handlers //////////
 
 
 
  // bind a keydown handler
 
  this.$items.keydown(function(e) {
 
    return thisObj.handleItemKeyDown($(this), e);
 
  });
 
 
  // bind a keypress handler
 
  this.$items.keypress(function(e) {
 
    return thisObj.handleItemKeyPress($(this), e);
 
  });
 
 
  // bind a focus handler
 
  this.$items.focus(function(e) {
 
    return thisObj.handleItemFocus($(this), e);
 
  });
 
 
  ///////// bind document handlers //////////
 
 
 
  // bind a click handler to close the menu when user clicks elsewhere
 
  $(document).click(function(e) {
 
    return thisObj.handleDocumentClick(e);
 
  });
 
 
} // end bindHandlers()
 
 
//
 
// Function handleMenuMouseOver() is a member function to process mouseover
 
// events for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleMenuMouseOver = function($menu, e) {
 
 
  // cancel the close timer if it is set
 
  if (g_closetimer) {
 
    window.clearTimeout(g_closetimer);
 
    g_closetimer = undefined;
 
  }
 
 
  // if there is an open menu, remove the highlight style from menu items
 
  // and close it
 
  if (this.$openMenu) {
 
 
    // if the open menu is not the one being processed, hide it
 
    if ($menu.get(0) != this.$openMenu.get(0)) {
 
      this.closeMenu(this.$openMenu, true);
 
    }
 
 
    // open the current menu
 
    this.showMenu($menu);
 
   
 
    // remove styling from all menus
 
    this.removeStyling(this.$menus);
 
 
    // if the current menu was not given focus, set focus.
 
    if ($menu.hasClass('.focus') == false) {
 
      $menu.focus();
 
    }
 
 
  }
 
  else {
 
    // Add hover style
 
    $menu.addClass('hover');
 
  }
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleMenuMouseOver()
 
 
//
 
// Function handleMenuMouseOut() is a member function to process mouseout
 
// events for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleMenuMouseOut = function($menu, e) {
 
 
  // set the close timer to call closeMenu
 
  g_closetimer = window.setTimeout(function() { menuApp.closeMenu($menu, false); }, this.timeout);
 
 
  // Remover hover style
 
  $menu.removeClass('hover');
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleMenuMouseOut()
 
 
//
 
// Function handleMenuClick() is a member function to process click events
 
// for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleMenuClick = function($menu, e) {
 
 
  // If a menu is open, close it
 
  if (this.$openMenu) {
 
    this.closeMenu(this.$openMenu, true);
 
  }
 
  else {
 
    this.showMenu($menu);
 
  }
 
 
  // remove styling from other menus
 
  this.removeStyling(this.$menus);
 
 
  // set focus on this menu
 
  $menu.focus();
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleMenuClick()
 
 
//
 
// Function handleMenuFocus() is a member function to process focus events
 
// for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns true;
 
//
 
menubar.prototype.handleMenuFocus = function($menu, e) {
 
 
  $menu.addClass('focus');
 
 
  return true;
 
 
} // end handleMenuFocus()
 
 
//
 
// Function handleMenuBlur() is a member function to process blur events
 
// for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns true;
 
//
 
menubar.prototype.handleMenuBlur = function($menu, e) {
 
 
  if (this.itemFocus == false) {
 
    // remove the focus and hover classes
 
    this.removeStyling($menu);
 
  }
 
  else {
 
    this.itemFocus = false;
 
  }
 
 
  return true;
 
 
} // end handleMenuBlur()
 
 
//
 
// Function handleMenuKeyDown() is a member function to process keydown events
 
// for the top menus.
 
//
 
// @param($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false if consuming; true if propagating
 
//
 
menubar.prototype.handleMenuKeyDown = function($menu, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
        // Modifier key pressed: Do not process
 
        return true;
 
    }
 
 
  switch(e.keyCode) {
 
    case this.keys.tab: {
 
      // close the open menu (if applicable)
 
      this.closeMenu(this.$openMenu, false);
 
 
      // allow tab event to propagate;
 
      return true;
 
    }
 
    case this.keys.esc: {
 
      // close the open menu (if applicable)
 
      this.closeMenu(this.$openMenu, true);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.enter:
 
    case this.keys.space:
 
    case this.keys.up:
 
    case this.keys.down: {
 
 
      // open the menu
 
      this.showMenu($menu);
 
 
      // set focus on the first menu item
 
      $menu.find('li:first').focus();
 
 
      // set itemFocus flag to prevent blur from processing (IE fix).
 
      this.itemFocus = true;
 
   
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.left: {
 
      // select the previous menu (rotate to
 
      // last menu if this is the first one).
 
 
      var curNdx = this.$menus.index($menu);
 
      var $prev;
 
     
 
      // find the menu to move to
 
      if (curNdx > 0) {
 
        $prev = this.$menus.eq(curNdx - 1);
 
      }
 
      else {
 
        $prev = this.$menus.last();
 
      }
 
 
      // open the new menu
 
      this.showMenu($prev);
 
 
      // set focus on the menu's first item
 
      $prev.find('li:first').focus();
 
 
      // close this menu if it is open
 
      this.closeMenu($menu, false);
 
 
      // Remove the focus styling
 
      this.removeStyling($menu);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
 
    case this.keys.right: {
 
      // select the next menu (rotate to
 
      // first menu if this is the last one).
 
 
      var curNdx = this.$menus.index($menu);
 
      var $next;
 
     
 
      if (curNdx < this.$menus.length - 1) {
 
        $next = this.$menus.eq(curNdx + 1);
 
      }
 
      else {
 
        $next = this.$menus.first();
 
      }
 
 
      // open the new menu
 
      this.showMenu($next);
 
 
      // set focus on the menu's first item
 
      $next.find('li:first').focus();
 
 
      // close this menu if it is open
 
      this.closeMenu($menu, false);
 
 
      // Remove the focus styling
 
      this.removeStyling($menu);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
 
  } // end switch
 
 
  return true;
 
 
} // end handleMenuKeyDown()
 
 
//
 
// Function handleMenuKeyPress() is a member function to process keydown events
 
// for the top 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($menu object) $menu is the jquery object of the menu firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false if consuming; true if propagating
 
//
 
menubar.prototype.handleMenuKeyPress = function($menu, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
      // Modifier key pressed: Do not process
 
      return true;
 
  }
 
 
  switch(e.keyCode) {
 
    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;
 
    }
 
 
  } // end switch
 
 
  return true;
 
 
} // end handleMenuKeyPress()
 
 
//
 
// Function handleItemMouseOver() is a member function to process mouseover
 
// events for the menu items.
 
//
 
// @param($item object) $item is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleItemMouseOver = function($item, e) {
 
 
  // cancel the close timer if it is set
 
  if (g_closetimer) {
 
    window.clearTimeout(g_closetimer);
 
    g_closetimer = undefined;
 
  }
 
 
  // add the hover class to the current item
 
  if ($item.attr('aria-checked') == 'true') {
 
    $item.addClass('hover-checked');
 
  }
 
  else {
 
    $item.addClass('hover');
 
  }
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleItemMouseOver()
 
 
//
 
// Function handleItemMouseOut() is a member function to process mouseout
 
// events for the menu items.
 
//
 
// @param($item object) $item is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleItemMouseOut = function($item, e) {
 
 
  // find the parent menu for this item
 
  var $menu = this.findParentMenu($item);
 
 
 
  // set the close timer to call closeMenu
 
  g_closetimer = window.setTimeout(function() { menuApp.closeMenu($menu, true); }, this.timeout);
 
 
  // remove the hover class from the menu item
 
  if ($item.attr('aria-checked') == 'true') {
 
    $item.removeClass('hover-checked');
 
  }
 
  else {
 
    $item.removeClass('hover');
 
  }
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleItemMouseOut()
 
 
//
 
// Function handleItemClick() is a member function to process click events
 
// for the menu items.
 
//
 
// @param($item object) $item is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false;
 
//
 
menubar.prototype.handleItemClick = function($item, e) {
 
 
  var $menu = this.findParentMenu($item);
 
  var thisObj = this;
 
 
  // remove the focus style from the item
 
  this.removeStyling($item);
 
 
  // proces the click
 
  this.processMenuChoice($item);
 
 
  // close the menu
 
  this.closeMenu($menu, false);
 
 
  // remove the focus styling from the menu
 
  this.removeStyling($menu);
 
 
  // set focus on the text area
 
  this.textarea.$id.focus();
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleItemClick()
 
 
//
 
// Function handleItemFocus() is a member function to process focus events
 
// for the menu items.
 
//
 
// @param($item object) $item is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns true;
 
//
 
menubar.prototype.handleItemFocus = function($item, e) {
 
 
  // apply the focus class
 
  if ($item.attr('aria-checked') == 'true') {
 
    $item.addClass('focus-checked');
 
  }
 
  else {
 
    $item.addClass('focus');
 
  }
 
 
  return true;
 
 
} // end handleItemFocus()
 
 
//
 
// Function removeStyling() is a member function to remove the focus styling
 
// for the menu items. This is necessary because browsers do not reliably fire the blur
 
// event for non-input elements.
 
//
 
// @param($item object) $item is the jquery object of the item to manipulate
 
//
 
// @return N/A
 
//
 
menubar.prototype.removeStyling = function($item) {
 
 
  // remove the focus class
 
  if ($item.attr('aria-checked') == 'true') {
 
    $item.removeClass('focus-checked hover-checked');
 
  }
 
  else {
 
    $item.removeClass('focus hover');
 
  }
 
 
} // end removeStyling()
 
 
//
 
// Function handleItemKeyDown() is a member function to process keydown events
 
// for the menu items.
 
//
 
// @param($item object) $menu is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false if consuming; true if propagating
 
//
 
menubar.prototype.handleItemKeyDown = function($item, e) {
 
 
  var $menu = this.findParentMenu($item);
 
 
  if (e.altKey || e.ctrlKey) {
 
        // Modifier key pressed: Do not process
 
        return true;
 
  }
 
  if (e.shiftKey) {
 
    if (e.keyCode == this.keys.tab) {
 
      // do nothing
 
      e.stopPropagation();
 
      return false;
 
    }
 
 
    // do not process
 
    return true;
 
  }
 
 
  switch(e.keyCode) {
 
    case this.keys.tab: {
 
 
      // close the open menu
 
      this.closeMenu($menu, false);
 
 
      // remove the focus style
 
      this.removeStyling($menu);
 
 
      // allow tab event to propagate;
 
      return true;
 
    }
 
    case this.keys.esc: {
 
 
      // close the open menu (if applicable)
 
      this.closeMenu($menu, true);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.enter:
 
    case this.keys.space: {
 
 
      // remove the focus style from the item
 
      this.removeStyling($item);
 
 
      // process the choice
 
      this.processMenuChoice($item);
 
 
      // close the menu
 
      this.closeMenu($menu, false);
 
 
      // remove the focus styling from the menu
 
      this.removeStyling($menu);
 
 
      // set focus on the text area
 
      this.textarea.$id.focus();
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.left: {
 
      // select the previous menu (rotate to
 
      // last menu if this is the first one).
 
 
      var curNdx = this.$menus.index($menu);
 
      var $prev;
 
     
 
      // find the menu to move to
 
      if (curNdx > 0) {
 
        $prev = this.$menus.eq(curNdx - 1);
 
      }
 
      else {
 
        $prev = this.$menus.last();
 
      }
 
 
      // open the new menu
 
      this.showMenu($prev);
 
 
      // set focus on the menu's first item
 
      $prev.find('li:first').focus();
 
 
      // close this menu
 
      this.closeMenu($menu, false);
 
 
      // remove the focus style
 
      this.removeStyling($menu);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.right: {
 
      // select the next menu (rotate to
 
      // first menu if this is the last one).
 
 
      var curNdx = this.$menus.index($menu);
 
      var $next;
 
     
 
      if (curNdx < this.$menus.length - 1) {
 
        $next = this.$menus.eq(curNdx + 1);
 
      }
 
      else {
 
        $next = this.$menus.first();
 
      }
 
 
      // open the new menu
 
      this.showMenu($next);
 
 
      // set focus on the menu's first item
 
      $next.find('li:first').focus();
 
 
      // close this menu
 
      this.closeMenu($menu, false);
 
 
      // remove the focus style
 
      this.removeStyling($menu);
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.up: {
 
      // select the previous menu item (rotate to
 
      // last item if this is the first one).
 
 
      var $listItems = $item.siblings().andSelf().not('.separator');
 
      var curNdx = $listItems.index($item);
 
      var $prev;
 
 
      // find the new item to select
 
      if (curNdx > 0) {
 
        $prev = $listItems.eq(curNdx - 1);
 
      }
 
      else {
 
        $prev = $listItems.last();
 
      }
 
 
      // remove highlighting from the menu item
 
      this.removeStyling($item);
 
 
      // set focus on the new item;
 
      $prev.focus();
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.down: {
 
      // select the next menu item (rotate to
 
      // first item if this is the first one).
 
 
      var $listItems = $item.siblings().andSelf().not('.separator');
 
      var curNdx = $listItems.index($item);
 
      var $next;
 
 
      // find the new item to select
 
      if (curNdx < $listItems.length - 1) {
 
        $next = $listItems.eq(curNdx + 1);
 
      }
 
      else {
 
        $next = $listItems.first();
 
      }
 
 
      // remove highlighting from the menu item
 
      this.removeStyling($item);
 
 
      // set focus on the new item;
 
      $next.focus();
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
 
  } // end switch
 
 
  return true;
 
 
} // end handleItemKeyDown()
 
 
//
 
// Function handleItemKeyPress() is a member function to process keydown events
 
// for the menu items.
 
//
 
// 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) $item is the jquery object of the menu item firing the event
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns false if consuming; true if propagating
 
//
 
menubar.prototype.handleItemKeyPress = function($item, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
      // Modifier key pressed: Do not process
 
      return true;
 
  }
 
 
  switch(e.keyCode) {
 
    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;
 
    }
 
 
  } // end switch
 
 
  return true;
 
 
} // end handleItemKeyPress()
 
 
//
 
// 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 menubar
 
//
 
// @param(e object) e is the associated event object
 
//
 
// @return(boolean) Returns true;
 
//
 
menubar.prototype.handleDocumentClick = function(e) {
 
 
  var $menu = this.$openMenu;
 
 
  if (this.$openMenu) {
 
    // set the close timer to call closeMenu
 
    g_closetimer = window.setTimeout(function() { menuApp.closeMenu($menu, false); }, this.timeout);
 
 
    // Remover styling
 
    this.removeStyling(this.$openMenu);
 
 
  }
 
 
  // allow the event to propagate
 
  return true;
 
 
} // end handleDocumentClick()
 
 
//
 
// Function processMenuChoice() is a member function to process the user's menu item
 
// choice. Since the menu will close after this, highlight styling is simply removed.
 
//
 
// @param($item object) $item is the jquery object of the menu item firing the event
 
//
 
// @return N/A
 
//
 
menubar.prototype.processMenuChoice = function($item) {
 
 
  // find the parent menu for this item
 
  var $menu = this.findParentMenu($item);
 
  var menuName = $item.parent().attr('id');
 
  var choice = $item.attr('id');
 
 
  // call the appropriate textarea function
 
  switch(menuName) {
 
    case 'fontMenu': {
 
      this.textarea.setFont(choice);
 
 
      // update the aria-checked state of the menu items and apply
 
      // appropriate styling
 
      $item.attr('aria-checked', 'true').addClass('checked');
 
      $item.siblings().attr('aria-checked', 'false').removeClass('checked');
 
 
      break;
 
    }
 
    case 'styleMenu': {
 
      this.textarea.setStyle(choice);
 
 
      // reverse the aria-checked state and update styling
 
      if ($item.attr('aria-checked') == 'true') {
 
        $item.attr('aria-checked', 'false').removeClass('checked');
 
      }
 
      else {
 
        $item.attr('aria-checked', 'true').addClass('checked');
 
      }
 
 
      break;
 
    }
 
    case 'justificationMenu': {
 
      this.textarea.setAlignment(choice);
 
 
      // update the aria-checked state of the menu items and apply
 
      // appropriate styling
 
      $item.attr('aria-checked', 'true').addClass('checked');
 
      $item.siblings().attr('aria-checked', 'false').removeClass('checked');
 
 
      break;
 
    }
 
    case 'sizeMenu': {
 
      var $menuItems = $item.siblings().andSelf();
 
      var $curItem;
 
 
      this.textarea.setSize(choice);
 
 
      ////////////////////////
 
      // update the aria-checked state by first setting all items to false
 
      // and then setting the item that matches the currently selected font
 
      // size.
 
     
 
      // set aria-checked to false and removed checked styling
 
      $menuItems.attr('aria-checked', 'false').removeClass('checked');
 
 
      // determine which menu item matches the current font size
 
      $curItem = $menuItems.filter('[id=' + this.textarea.getSize() + ']');
 
 
      // Set aria-checked for the matching item and apply the checked styling
 
      $curItem.attr('aria-checked', 'true').addClass('checked');
 
 
      break;
 
    }
 
  } // end switch
 
 
} // end processMenuChoice()
 
 
/////////////// end menubar widget definition /////////////////////
 
  </script>
 
 
</head>
 
</head>
 
<body>
 
<body>
<div role="application">
+
<div role="menubar">  
 
+
<!--
<ul id="mb1" class="menubar" role="menubar" title="Styling Menu" aria-controls="st1">
+
File menu: File menuitem has an aria-haspopup attribute set to 'true'.
  <li id="mb1_menu1" role="menuitem" tabindex="0" aria-haspopup="true">
+
That popup div follows immediately below.
    Font
+
-->
    <ul id="fontMenu" class="menu" role="menu">
+
<div role="menuitem" aria-haspopup="true" id="fileMenu">File</div>
      <li id="sans-serif"
+
<div role="menu" aria-labelledby="fileMenu">
        role="menuitemradio"
+
<div role="menuitem">Open</div>
        tabindex="-1"
+
<div role="menuitem">Save</div>
        aria-controls="st1"
+
<div role="menuitem">Save as ...</div>
        aria-checked="true">
+
</div>
          Sans-serif
+
      </li>
+
<!--
      <li id="serif"
+
View menu:
        role="menuitemradio"
+
-->
        tabindex="-1"
+
<div role="menuitem" aria-haspopup="true" id="viewMenu">View</div>
        aria-controls="st1"
+
<div role="menu" aria-labelledby="viewMenu">
        aria-checked="false">
+
<div role="menuitem">Normal</div>
          Serif
+
<div role="menuitem">Outline</div>
      </li>
+
      <li id="monospace"
+
<!--
        role="menuitemradio"
+
The View's Zoom menuitem has aria-haspopup='true' as it leads to a submenu.
        tabindex="-1"
+
-->
        aria-controls="st1"
+
<div role="menuitem" aria-haspopup="true" id="zoomSubMenu">Zoom</div>
        aria-checked="false">
+
<div role="menu" aria-labelledby="zoomSubMenu">
          Monospace
+
<div role="menuitem">50%</div>
      </li>
+
<div role="menuitem">75%</div>
      <li id="fantasy"
+
<div role="menuitem">100%</div>
        role="menuitemradio"
+
<div role="menuitem">150%</div>
        tabindex="-1"
+
<div role="menuitem">200%</div>
        aria-controls="st1"
+
</div>
        aria-checked="false">
+
</div>
          Fantasy
+
</div>
      </li>
 
    </ul>
 
  </li>
 
  <li id="mb1_menu2" role="menuitem" tabindex="-1" aria-haspopup="true">
 
    Style
 
    <ul id="styleMenu" class="menu" role="menu">
 
      <li id="italic"
 
        role="menuitemcheckbox"
 
        aria-controls="st1"
 
  aria-checked="false"
 
        tabindex="-1">
 
          Italics
 
      </li>
 
      <li id="bold"
 
        role="menuitemcheckbox"
 
        aria-controls="st1"
 
  aria-checked="false"
 
        tabindex="-1">
 
          Bold
 
      </li>
 
      <li id="underline"
 
        role="menuitemcheckbox"
 
        aria-controls="st1"
 
  aria-checked="false"
 
        tabindex="-1">
 
          Underlined
 
      </li>
 
    </ul>
 
  </li>
 
  <li id="mb1_menu3" role="menuitem" tabindex="-1" aria-haspopup="true">
 
    Justification
 
    <ul id="justificationMenu" class="menu" role="menu" title="Justication">
 
      <li id="left"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="true">
 
          Left
 
      </li>
 
      <li id="center"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Centered
 
      </li>
 
      <li id="right"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Right
 
      </li>
 
      <li id="justify"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Justify
 
      </li>
 
    </ul>
 
  </li>
 
  <li id="mb1_menu4" role="menuitem" tabindex="-1" aria-haspopup="true">
 
    Size
 
    <ul id="sizeMenu" class="menu" role="menu" title="Size">
 
      <li id="larger"
 
        role="menuitem"
 
        aria-controls="st1"
 
        tabindex="-1">
 
          Larger
 
      </li>
 
      <li id="smaller"
 
        role="menuitem"
 
        aria-controls="st1"
 
        tabindex="-1">
 
          Smaller
 
      </li>
 
      <li id="fs_separator"
 
        class="separator"
 
        role="separator"
 
        tabindex="-1">
 
      </li>
 
      <li id="x-small"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          X-Small
 
      </li>
 
      <li id="small"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Small
 
      </li>
 
      <li id="medium"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="true">
 
          Medium
 
      </li>
 
      <li id="large"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Large
 
      </li>
 
      <li id="x-large"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          X-Large
 
      </li>
 
    </ul>
 
  </li>
 
</ul>
 
 
 
</div>
 
 
</body>
 
</body>
 
</html>
 
</html>
 
[[Category:ARIA]]
 
[[Category:ARIA]]

Revision as of 15:38, 9 April 2014

ARIA Menu Example

Open
Save
Save as ...
Normal
Outline
50%
75%
100%
150%
200%