Difference between revisions of "ARIA Menu"

From Level Access Web Labs
Jump to navigation Jump to search
 
(28 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<html lang="en-US">
+
This example is not working correctly.
<head>
 
<title>ARIA Menu Example</title>
 
  <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 {
+
<html lang="en">
  margin: 0;
+
  <head>
   margin-bottom: 4px !important;
+
   <title>
  height: 2px !important;
+
    ARIA menus
   border-bottom: 1px solid black;
+
   </title>
}
+
   <style type="text/css">
 
+
    #l1, #l2 {
li.checked {
+
       float:left;
  font-weight: bold;
+
       width:10em;
  background-image: url('images/dot.png');
+
       background-color:darkblue;
  background-repeat: no-repeat;
+
       color:white;
  background-position: 5px 10px;
+
       padding:.2em;
}
 
 
 
.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();
 
 
     }
 
     }
  }
+
     li {
 
+
      list-style-type:none !important;
  // 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);
 
 
     }
 
     }
 
+
     #label1 {
     // 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();
 
 
     }
 
     }
 
+
     #u1, #u2 {
  }
+
      display:none;
  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;
 
 
     }
 
     }
 
+
    .focus {
  switch(e.keyCode) {
+
    outline: dotted;
    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: {
+
  </style>
      // close the open menu (if applicable)
+
  <script type="text/javascript">
      this.closeMenu(this.$openMenu, true);
+
     function showHide(evt) {
 
+
       var e = window.event ? window.event : evt;
      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
+
            obj = e.target;
      this.removeStyling($menu);
+
            toggle(obj,1);
 
 
      e.stopPropagation();
 
      return false;
 
 
     }
 
     }
  
  } // end switch
+
        function visible(obj) {
 +
            if (window.getComputedStyle(obj,null).display == "none")
 +
                return false;
 +
            else
 +
              return true;
 +
        }
  
  return true;
+
        // set force to 1 to force only that one to be displayed
 +
    function toggle(obj, force) {
  
} // end handleMenuKeyDown()
+
                if (obj.tagName == "SPAN")
 +
                  obj = obj.parentNode;
  
//
+
            if (obj.id == "l1" || obj.id == "l2") {
// Function handleMenuKeyPress() is a member function to process keydown events
+
                obj = obj.children[1]
// 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) {
+
          if (obj.tagName == "UL") {
      // Modifier key pressed: Do not process
+
                if (force) {
      return true;
+
                        var o = document.getElementsByClassName('menu')
  }
+
                        for (var i=0; i < o.length; i++) {
  
  switch(e.keyCode) {
+
                          o[i].style.display = "none";
    case this.keys.esc:
+
                        }
    case this.keys.enter:
+
                  obj.style.display = "block";
    case this.keys.space:
+
            }
    case this.keys.up:
+
                else if (!visible(obj)) {
    case this.keys.down:
+
                  obj.style.display = "block";
    case this.keys.left:
+
                  obj.setAttribute("aria-expanded","true");
    case this.keys.right: {
+
                  obj.querySelector("li").focus();
 
+
                }
      e.stopPropagation();
+
                else
      return false;
+
                    obj.style.display = "none";
 +
          }
 +
          else if (obj.tagName == "LI") {
 +
            obj.onclick();
 +
          }
 
     }
 
     }
  
  } // end switch
+
    function drawFocus() {
 +
      removeFocus();
  
  return true;
+
      var id = document.getElementById('m1').getAttribute('aria-activedescendant');
 +
      var obj = document.getElementById(id);
  
} // end handleMenuKeyPress()
+
      if (!visible(obj)) {
 
+
            toggle(obj,0);
//
 
// 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 {
+
       if (!visible(obj.parentNode)) {
        $prev = this.$menus.last();
+
            toggle(obj.parentNode,0);
 
       }
 
       }
 
+
       if (document.getElementById(id).className)
       // open the new menu
+
        document.getElementById(id).className += " focus";
      this.showMenu($prev);
+
       else
 
+
         document.getElementById(id).className = "focus";
      // 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
+
    function removeFocus() {
 +
            var o = document.getElementsByClassName('focus')
  
  return true;
+
            for(var i = 0; i < o.length; i++) {
 +
                o[i].className = o[i].className.replace( /(?:^|\s)focus(?!\S)/g,'');
 +
            }
  
} // 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': {
+
     function keystroke(evt) {
       this.textarea.setStyle(choice);
+
       var e = window.event ? window.event : evt;
 +
            var activeID = document.getElementById('m1').getAttribute('aria-activedescendant');
  
      // reverse the aria-checked state and update styling
+
            if (e.which == 13) { // enter
      if ($item.attr('aria-checked') == 'true') {
+
                toggle(document.getElementById(activeID),0);
        $item.attr('aria-checked', 'false').removeClass('checked');
+
            }
      }
+
            else if (e.which == 40) { // down arrow
      else {
+
          document.getElementById('m1').setAttribute('aria-activedescendant',document.getElementById(activeID).getAttribute('data-next'));
        $item.attr('aria-checked', 'true').addClass('checked');
+
          drawFocus();
      }
+
            }
 
+
            else if (e.which == 38) { // up arrow
      break;
+
          document.getElementById('m1').setAttribute('aria-activedescendant',document.getElementById(activeID).getAttribute('data-previous'));
    }
+
          drawFocus();
    case 'justificationMenu': {
+
            }
      this.textarea.setAlignment(choice);
+
            else if (e.which == 39) {            // right arrow
 
+
                    if (activeID.indexOf("l1") > -1) {
      // update the aria-checked state of the menu items and apply
+
              toggle(document.getElementById('l2'),1);
      // appropriate styling
+
            document.getElementById('m1').setAttribute('aria-activedescendant',"l2");
      $item.attr('aria-checked', 'true').addClass('checked');
+
          }
      $item.siblings().attr('aria-checked', 'false').removeClass('checked');
+
          else {
 
+
              toggle(document.getElementById('l1'),1);
      break;
+
            document.getElementById('m1').setAttribute('aria-activedescendant',"l1");
    }
+
          }
    case 'sizeMenu': {
+
          drawFocus();
      var $menuItems = $item.siblings().andSelf();
+
            }
      var $curItem;
+
            else if (e.which == 37) {            // left arrow
 
+
                    if (activeID.indexOf("l1") > -1) {
      this.textarea.setSize(choice);
+
              toggle(document.getElementById('l2'),1);
 
+
            document.getElementById('m1').setAttribute('aria-activedescendant',"l2");
      ////////////////////////
+
          }
      // update the aria-checked state by first setting all items to false
+
          else {
      // and then setting the item that matches the currently selected font
+
                toggle(document.getElementById('l1'),1);
      // size.
+
            document.getElementById('m1').setAttribute('aria-activedescendant',"l1");
     
+
          }
      // set aria-checked to false and removed checked styling
+
          drawFocus();
      $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>
 
   </script>
</head>
+
  </head>
 
<body>
 
<body>
<div role="application">
 
  
<ul id="mb1" class="menubar" role="menubar" title="Styling Menu" aria-controls="st1">
+
<h2>Menubar with menu items that contain menus with other menu items</h2>
  <li id="mb1_menu1" role="menuitem" tabindex="0" aria-haspopup="true">
+
<div>
    Font
+
<ul tabindex="0" onclick="showHide(event);" onkeyup="keystroke(event);" id="m1" onfocus="drawFocus();" onblur="removeFocus();" role="menubar" aria-activedescendant="l1" >
    <ul id="fontMenu" class="menu" role="menu">
+
  <li id="l1" role="menuitem" aria-haspopup="true" aria-labelledby="label1" data-next="l1a" data-previous="l1c"><span id="label1" aria-expanded="false" >File &#9660;</span>
      <li id="sans-serif"
+
    <ul role="menu" id="u1" class="menu" >
        role="menuitemradio"
+
      <li tabindex="-1" id="l1a" data-next="l1b" data-previous="l1" onclick="alert('Saving file');" role="menuitem">Save</li>
        tabindex="-1"
+
      <li tabindex="-1" id="l1b" data-next="l1c" data-previous="l1a" onclick="alert('Saving file as ...');" role="menuitem">Save as ...</li>
        aria-controls="st1"
+
      <li tabindex="-1" id="l1c" data-next="l1" data-previous="l1b" onclick="alert('Printing file');" role="menuitem">Print</li>
        aria-checked="true">
+
    </ul>
          Sans-serif
+
  </li>
      </li>
+
  <li id="l2" tabindex="-1" role="menuitem" aria-haspopup="true" aria-labeleldby="label2" data-next="l2a" data-previous="l2c"><span id="label2" aria-expanded="false">Review</span>
      <li id="serif"
+
    <ul role="menu" id="u2" class="menu" >
        role="menuitemradio"
+
      <li id="l2a" data-next="l2b" data-previous="l2" tabindex="-1" onclick="alert('Checking spelling');" role="menuitem">Spelling</li>
        tabindex="-1"
+
      <li id="l2b" data-next="l2c" data-previous="l2a" tabindex="-1" onclick="alert('Opening thesaurus');" role="menuitem">Thesaurus</li>
        aria-controls="st1"
+
      <li id="l2c" data-next="l2" data-previous="l2b" tabindex="-1" onclick="alert('Checking Accessibility');" role="menuitem">Accessibility</li>
        aria-checked="false">
+
    </ul>
          Serif
+
  </li>
      </li>
+
</ul>
      <li id="monospace"
+
 
        role="menuitemradio"
+
</div>
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Monospace
 
      </li>
 
      <li id="fantasy"
 
        role="menuitemradio"
 
        tabindex="-1"
 
        aria-controls="st1"
 
        aria-checked="false">
 
          Fantasy
 
      </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>
+
<div style="clear:both; padding-top:4em;">&nbsp;</div>
</body>
 
 
</html>
 
</html>
[[Category:ARIA]]
+
 
 +
[[category:ARIA]]

Latest revision as of 15:09, 7 March 2018

This example is not working correctly.

ARIA menus

Menubar with menu items that contain menus with other menu items