Alert Navigation Menu
Menubar Example 1
Keyboard Shortcuts
The following keyboard shortcuts are implemented for this example (based on recommended shortcuts specified by the DHTML Style Guide Working Group.):
If focus is on the menubar:
- Left arrow: Next menubar item
- Right arrow: Previous menubar item
- Up arrow: Open pull down menu and select first menu item
- Down arrow: Open pull down menu and select first menu item
- Enter: Open or close pull down menu. Select first menu item if opening
- Space: Open or close pull down menu. Select first menu item if opening
If focus is on a menu item:
- Left arrow: Open next pull down menu and select first item
- Right arrow: Open previous pull menu and select first item
- Up arrow: Select previous menu item
- Down arrow: Select next menu item
- Enter: Invoke selected item and dismiss menu
- Space: Invoke selected item and dismiss menu
- Esc: Close menu and return focus to menubar
ARIA Roles and Properties
-
Roles:
role="application"
role="menubar"
role="menu"
role="menuitem"
role="menuitemradio"
role="menuitemcheckbox"
-
States and properties:
aria-checked
aria-controls
aria-haspopup
HTML Source Code
Show HTML Source Code: menubar1.inc
<div role="application">
<ul id="mb1" class="menubar" role="menubar" title="Styling Menu" aria-controls="st1">
<li id="mb1_menu1" role="menuitem" tabindex="0" aria-haspopup="true">
Font
<ul id="fontMenu" class="menu" role="menu">
<li id="sans-serif"
role="menuitemradio"
tabindex="-1"
aria-controls="st1"
aria-checked="true">
Sans-serif
</li>
<li id="serif"
role="menuitemradio"
tabindex="-1"
aria-controls="st1"
aria-checked="false">
Serif
</li>
<li id="monospace"
role="menuitemradio"
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>
<textarea id="st1" name="st1">
Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.
Now we are engaged in a great civil war, testing whether that nation, or any nation, so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.
But, in a larger sense, we can not dedicate, we can not consecrate, we can not hallow, this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us, that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion, that we here highly resolve that these dead shall not have died in vain, that this nation, under God, shall have a new birth of freedom, and that government of the people, by the people, for the people, shall not perish from the earth.
</textarea>
<p><a href="http://en.wikipedia.org/wiki/Gettysburg,_Pennsylvania">More information on Gettysburg Address</a></p>
</div>
Javascript Source Code
Show Javascript Source Code: menubar1.js
<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>
CSS Source Code
Show CSS Source Code: menubar1.css
<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>
W3C Validation of HTML5