Skip to content

Commit

Permalink
Menu Button Examples: Add activedescendant example and fix multiple b…
Browse files Browse the repository at this point in the history
…ugs (pull #375)

Made the following changes to the menu button examples:

1. Added an example that uses aria-activedescendant to manage focus in the menu; there are now three example pages instead of two.
2. For issue #363, changed how aria-expanded is implemented.
3. fixed a formatting bug.
4. Fixed link execution bugs; enter, ctrl+enter, and shift+enter all work as expected in the navigation menu example.
5. Fixed alt, ctrl, and meta key modifier bugs; Key events that include these key events are no longer captured and ignored by the JavaScript.
6. Put all three example HTML files in the same directory.
7. Made numerous editorial corrections and revisions to improve accuracy and clarity.
  • Loading branch information
jongund authored and mcking65 committed Apr 26, 2017
1 parent 81865eb commit 35cd0b9
Show file tree
Hide file tree
Showing 13 changed files with 1,106 additions and 382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ input:hover {
}

ul[role="menu"] li[role="menuitem"]:focus,
ul[role="menu"] li[role="menuitem"].focus,
ul[role="menu"] li[role="menuitem"]:hover{
background-color: black;
color: white;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,80 +73,92 @@ MenuItem.prototype.handleKeydown = function (event) {
return str.length === 1 && str.match(/\S/);
}

switch (event.keyCode) {
case this.keyCode.SPACE:
case this.keyCode.RETURN:
// Create simulated mouse event to mimic the behavior of ATs
// and let the event handler handleClick do the housekeeping.
try {
clickEvent = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
}
catch (err) {
if (document.createEvent) {
// DOM Level 3 for IE 9+
clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
}
}
tgt.dispatchEvent(clickEvent);
flag = true;
break;

case this.keyCode.ESC:
this.menu.setFocusToController();
this.menu.close(true);
flag = true;
break;

case this.keyCode.UP:
this.menu.setFocusToPreviousItem(this);
flag = true;
break;

case this.keyCode.DOWN:
this.menu.setFocusToNextItem(this);
flag = true;
break;

case this.keyCode.LEFT:
this.menu.setFocusToController('previous');
this.menu.close(true);
flag = true;
break;

case this.keyCode.RIGHT:
this.menu.setFocusToController('next');
this.menu.close(true);
flag = true;
break;

case this.keyCode.HOME:
case this.keyCode.PAGEUP:
this.menu.setFocusToFirstItem();
flag = true;
break;

case this.keyCode.END:
case this.keyCode.PAGEDOWN:
this.menu.setFocusToLastItem();
flag = true;
break;

case this.keyCode.TAB:
this.menu.setFocusToController();
this.menu.close(true);
break;

default:
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
break;
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}

if (event.shiftKey) {
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
}
else {

switch (event.keyCode) {
case this.keyCode.SPACE:
case this.keyCode.RETURN:
// Create simulated mouse event to mimic the behavior of ATs
// and let the event handler handleClick do the housekeeping.
try {
clickEvent = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
}
catch (err) {
if (document.createEvent) {
// DOM Level 3 for IE 9+
clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
}
}
tgt.dispatchEvent(clickEvent);
flag = true;
break;

case this.keyCode.ESC:
this.menu.setFocusToController();
this.menu.close(true);
flag = true;
break;

case this.keyCode.UP:
this.menu.setFocusToPreviousItem(this);
flag = true;
break;

case this.keyCode.DOWN:
this.menu.setFocusToNextItem(this);
flag = true;
break;

case this.keyCode.LEFT:
this.menu.setFocusToController('previous');
this.menu.close(true);
flag = true;
break;

case this.keyCode.RIGHT:
this.menu.setFocusToController('next');
this.menu.close(true);
flag = true;
break;

case this.keyCode.HOME:
case this.keyCode.PAGEUP:
this.menu.setFocusToFirstItem();
flag = true;
break;

case this.keyCode.END:
case this.keyCode.PAGEDOWN:
this.menu.setFocusToLastItem();
flag = true;
break;

case this.keyCode.TAB:
this.menu.setFocusToController();
this.menu.close(true);
break;

default:
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
break;
}
}

if (flag) {
event.stopPropagation();
Expand Down
59 changes: 59 additions & 0 deletions examples/menu-button/js/MenuItemActionActivedescendant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
* File: MenuItem.js
*
* Desc: Popup Menu Menuitem widget that implements ARIA Authoring Practices
*
* Author: Jon Gunderson, Ku Ja Eun, Nicholas Hoyt and Brian Loh
*/

/*
* @constructor MenuItem
*
* @desc
* Wrapper object for a simple menu item in a popup menu
*
* @param domNode
* The DOM element node that serves as the menu item container.
* The menuObj PopupMenu is responsible for checking that it has
* requisite metadata, e.g. role="menuitem".
*
* @param menuObj
* The object that is a wrapper for the PopupMenu DOM element that
* contains the menu item DOM element. See PopupMenuAction.js
*/
var MenuItem = function (domNode, menuObj) {
this.domNode = domNode;
this.menu = menuObj;
};

MenuItem.prototype.init = function () {
this.domNode.tabIndex = -1;

if (!this.domNode.getAttribute('role')) {
this.domNode.setAttribute('role', 'menuitem');
}

this.domNode.addEventListener('click', this.handleClick.bind(this));
this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this));
this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this));
};

/* EVENT HANDLERS */

MenuItem.prototype.handleClick = function (event) {
this.menu.setFocusToController();
this.menu.close(true);
};

MenuItem.prototype.handleMouseover = function (event) {
this.menu.hasHover = true;
this.menu.open();
};

MenuItem.prototype.handleMouseout = function (event) {
this.menu.hasHover = false;
setTimeout(this.menu.close.bind(this.menu, false), 300);
};
Original file line number Diff line number Diff line change
Expand Up @@ -73,81 +73,76 @@ MenuItemLinks.prototype.handleKeydown = function (event) {
return str.length === 1 && str.match(/\S/);
}

switch (event.keyCode) {
case this.keyCode.SPACE:
case this.keyCode.RETURN:
// Create simulated mouse event to mimic the behavior of ATs
// and let the event handler handleClick do the housekeeping.
try {
clickEvent = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
}
catch (err) {
if (document.createEvent) {
// DOM Level 3 for IE 9+
clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
if (event.ctrlKey ||
event.altKey ||
event.metaKey ||
(event.keyCode === this.keyCode.SPACE) ||
(event.keyCode === this.keyCode.RETURN)) {
return;
}

if (event.shiftKey) {
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
}
else {
switch (event.keyCode) {

case this.keyCode.ESC:
this.menu.setFocusToController();
this.menu.close(true);
flag = true;
break;

case this.keyCode.UP:
this.menu.setFocusToPreviousItem(this);
flag = true;
break;

case this.keyCode.DOWN:
this.menu.setFocusToNextItem(this);
flag = true;
break;

case this.keyCode.LEFT:
this.menu.setFocusToController('previous');
this.menu.close(true);
flag = true;
break;

case this.keyCode.RIGHT:
this.menu.setFocusToController('next');
this.menu.close(true);
flag = true;
break;

case this.keyCode.HOME:
case this.keyCode.PAGEUP:
this.menu.setFocusToFirstItem();
flag = true;
break;

case this.keyCode.END:
case this.keyCode.PAGEDOWN:
this.menu.setFocusToLastItem();
flag = true;
break;

case this.keyCode.TAB:
this.menu.setFocusToController();
this.menu.close(true);
break;

default:
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
}
tgt.dispatchEvent(clickEvent);
flag = true;
break;

case this.keyCode.ESC:
this.menu.setFocusToController();
this.menu.close(true);
flag = true;
break;

case this.keyCode.UP:
this.menu.setFocusToPreviousItem(this);
flag = true;
break;

case this.keyCode.DOWN:
this.menu.setFocusToNextItem(this);
flag = true;
break;

case this.keyCode.LEFT:
this.menu.setFocusToController('previous');
this.menu.close(true);
flag = true;
break;

case this.keyCode.RIGHT:
this.menu.setFocusToController('next');
this.menu.close(true);
flag = true;
break;

case this.keyCode.HOME:
case this.keyCode.PAGEUP:
this.menu.setFocusToFirstItem();
flag = true;
break;

case this.keyCode.END:
case this.keyCode.PAGEDOWN:
this.menu.setFocusToLastItem();
flag = true;
break;

case this.keyCode.TAB:
this.menu.setFocusToController();
this.menu.close(true);
break;

default:
if (isPrintableCharacter(char)) {
this.menu.setFocusByFirstCharacter(this, char);
}
break;
break;
}
}


if (flag) {
event.stopPropagation();
event.preventDefault();
Expand Down
Loading

0 comments on commit 35cd0b9

Please sign in to comment.