diff --git a/examples/index.html b/examples/index.html index 2874c16147..727de014bb 100644 --- a/examples/index.html +++ b/examples/index.html @@ -51,6 +51,7 @@

Examples by Role

banner @@ -98,6 +99,7 @@

Examples by Role

contentinfo @@ -222,6 +224,7 @@

Examples by Role

navigation @@ -468,6 +471,7 @@

Examples By Properties and States

@@ -612,6 +616,7 @@

Examples By Properties and States

  • Actions Menu Button Using aria-activedescendant
  • Actions Menu Button Using element.focus()
  • Navigation Menu Button
  • +
  • Navigation Menubar
  • Radio Group Using aria-activedescendant
  • Radio Group Using Roving tabindex
  • Date Picker Spin Button
  • diff --git a/examples/menubar/css/menubar-navigation.css b/examples/menubar/css/menubar-navigation.css index dddce81d00..9105c100a0 100644 --- a/examples/menubar/css/menubar-navigation.css +++ b/examples/menubar/css/menubar-navigation.css @@ -1,13 +1,51 @@ +.page header { + border: #005a9c solid 2px; + background: #005a9c; + color: white; + text-align: center; +} + +.page header .title { + font-size: 2.5em; + font-weight: bold; + font-family: serif; +} + +.page header .tagline { + font-style: italic; +} + +.page .main { + padding-top: 1em; + padding-left: 5%; + padding-right: 5%; + padding-bottom: 5em; + border-left: 2px solid #eee; + border-right: 2px solid #eee; +} + +.page footer { + border: #005a9c solid 2px; + background: #005a9c; + font-family: serif; + color: white; + font-style: italic; + padding-left: 1em; +} + +.page nav { + margin: 0; + padding: 0; + border: 2px solid #eee; +} + .menubar-navigation { margin: 0; - margin-top: 0.5em; - margin-bottom: 0.5em; - padding: 7px; + padding: 2px; font-size: 110%; list-style: none; background-color: #eee; border: #eee solid 1px; - border-radius: 5px; } .menubar-navigation li { @@ -30,16 +68,22 @@ .menubar-navigation [role="menu"] [role="separator"] { display: block; width: 12em; - margin: 0; + margin: 2px; + padding: 4px; + padding-left: 8px; + background-color: #eee; + border: 0 solid #eee; + color: black; } -.menubar-navigation [role="menuitem"], -.menubar-navigation [role="separator"] { - padding: 6px; +.menubar-navigation > li > [role="menuitem"] { + display: inline-block; + margin: 2px; + padding: 4px; + padding-bottom: 8px; background-color: #eee; border: 0 solid #eee; color: black; - border-radius: 5px; } .menubar-navigation [role="menuitem"] svg { @@ -65,10 +109,6 @@ transform: rotate(90deg) translate(5px, -5px); } -.menubar-navigation > li > [role="menuitem"] { - display: inline-block; -} - .menubar-navigation [role="menu"] { display: none; position: absolute; @@ -76,7 +116,6 @@ padding: 0; padding: 7px 4px; border: 2px solid #034575; - border-radius: 5px; background-color: #eee; } @@ -87,28 +126,74 @@ .menubar-navigation [role="separator"] { padding-top: 3px; - background-image: url("../images/separator.svg"); + background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cline x1='0' y1='6' x2='12' y2='6' style='stroke:black;stroke-width:1' /%3E%3C/svg%3E%0A"); background-position: center; background-repeat: repeat-x; } +/* aria-current styling */ + +.menubar-navigation [role="menu"] [role="menuitem"].aria-current-path, +.menubar-navigation [role="menu"] [role="menuitem"][aria-current] { + padding-left: 4px; + border-left: 4px solid #034575; +} + +.menubar-navigation > li > [role="menuitem"][aria-current], +.menubar-navigation > li > [role="menuitem"].aria-current-path { + padding-bottom: 2px; + border-bottom: 4px solid #034575; +} + /* focus styling */ .menubar-navigation.focus { - padding: 6px; - border: #034575 solid 2px; + padding: 0; + border: #034575 solid 3px; +} + +.menubar-navigation [role="menu"] [aria-expanded="true"], +.menubar-navigation [role="menu"] [role="menuitem"]:focus, +.menubar-navigation [role="menu"] [role="menuitem"]:hover { + outline: none; + background-color: #ccc; +} + +.menubar-navigation [role="menu"] [role="menuitem"]:focus, +.menubar-navigation [role="menu"] [role="menuitem"]:hover { + padding: 2px; + padding-left: 6px; + border: 2px solid #034575; } -.menubar-navigation [role="menuitem"][aria-expanded="true"], -.menubar-navigation [role="menuitem"]:focus, -.menubar-navigation [role="menuitem"]:hover { - background: #034575; - color: #fff; +.menubar-navigation [role="menu"] [aria-expanded="true"].aria-current-path, +.menubar-navigation [role="menu"] [role="menuitem"].aria-current-path:focus, +.menubar-navigation [role="menu"] [role="menuitem"].aria-current-path:hover, +.menubar-navigation [role="menu"] [role="menuitem"][aria-current]:focus, +.menubar-navigation [role="menu"] [role="menuitem"][aria-current]:hover { + padding-left: 4px; + border-left: 4px solid #034575; +} + +.menubar-navigation > li > [aria-expanded="true"], +.menubar-navigation > li > [role="menuitem"]:focus, +.menubar-navigation > li > [role="menuitem"]:hover { outline: none; + background-color: #ccc; } -.menubar-navigation [role="menuitem"]:focus, -.menubar-navigation [role="menuitem"]:hover { +.menubar-navigation > li > [role="menuitem"]:focus, +.menubar-navigation > li > [role="menuitem"]:hover { padding: 2px; - border: 4px solid #034575; + padding-bottom: 4px; + border: 2px solid #034575; +} + +.menubar-navigation > li > [aria-expanded="true"].aria-current-path, +.menubar-navigation > li > [role="menuitem"].aria-current-path:focus, +.menubar-navigation > li > [role="menuitem"].aria-current-path:hover, +.menubar-navigation > li > [role="menuitem"][aria-current]:focus, +.menubar-navigation > li > [role="menuitem"][aria-current]:hover { + padding-bottom: 2px; + border-bottom: 4px solid #034575; } diff --git a/examples/menubar/js/menubar-navigation.js b/examples/menubar/js/menubar-navigation.js index 398ae52db6..858871e134 100644 --- a/examples/menubar/js/menubar-navigation.js +++ b/examples/menubar/js/menubar-navigation.js @@ -9,605 +9,725 @@ 'use strict'; -var MenubarNavigation = function (domNode) { - this.domNode = domNode; - - this.popups = []; - this.menuitemGroups = {}; - this.menuOrientation = {}; - this.isPopup = {}; - this.isPopout = {}; - this.openPopups = false; +class NavigationContentGenerator { + constructor(siteURL, siteName) { + this.siteName = siteName; + this.siteURL = siteURL; + this.fillerTextSentences = []; + + this.fillerTextSentences.push( + 'The content on this page is associated with the $linkName link for $siteName.' + ); + } - this.firstChars = {}; // see Menubar init method - this.firstMenuitem = {}; // see Menubar init method - this.lastMenuitem = {}; // see Menubar init method + renderParagraph(linkURL, linkName) { + var content = ''; + this.fillerTextSentences.forEach( + (s) => + (content += s + .replace('$siteName', this.siteName) + .replace('$siteURL', this.siteURL) + .replace('$linkName', linkName) + .replace('$linkURL', linkURL)) + ); + return content; + } +} - this.initMenu(domNode, 0); +class MenubarNavigation { + constructor(domNode) { + var linkURL, linkTitle; - domNode.addEventListener('focusin', this.handleMenubarFocusin.bind(this)); - domNode.addEventListener('focusout', this.handleMenubarFocusout.bind(this)); + this.domNode = domNode; - window.addEventListener( - 'mousedown', - this.handleBackgroundMousedown.bind(this), - true - ); -}; + this.menuitems = []; + this.popups = []; + this.menuitemGroups = {}; + this.menuOrientation = {}; + this.isPopup = {}; + this.isPopout = {}; + this.openPopups = false; -MenubarNavigation.prototype.getMenuitems = function (domNode, depth) { - var nodes = []; + this.firstChars = {}; // see Menubar init method + this.firstMenuitem = {}; // see Menubar init method + this.lastMenuitem = {}; // see Menubar init method - var initMenu = this.initMenu.bind(this); - var popups = this.popups; + this.initMenu(domNode, 0); - function findMenuitems(node) { - var role, flag; + domNode.addEventListener('focusin', this.handleMenubarFocusin.bind(this)); + domNode.addEventListener('focusout', this.handleMenubarFocusout.bind(this)); - while (node) { - flag = true; - role = node.getAttribute('role'); + window.addEventListener( + 'mousedown', + this.handleBackgroundMousedown.bind(this), + true + ); - if (role) { - role = role.trim().toLowerCase(); - } + domNode.querySelector('[role=menuitem]').tabIndex = 0; - switch (role) { - case 'menu': - node.tabIndex = -1; - initMenu(node, depth + 1); - flag = false; - break; + // Initial content for page + if (location.href.split('#').length > 1) { + linkURL = location.href; + linkTitle = getLinkNameFromURL(location.href); + } else { + linkURL = location.href + '#home'; + linkTitle = 'Home'; + } - case 'menuitem': - if (node.getAttribute('aria-haspopup') === 'true') { - popups.push(node); - } - nodes.push(node); - break; + this.contentGenerator = new NavigationContentGenerator( + '#home', + 'Mythical University' + ); + this.updateContent(linkURL, linkTitle, false); - default: - break; + function getLinkNameFromURL(url) { + function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); } - if ( - flag && - node.firstElementChild && - node.firstElementChild.tagName !== 'svg' - ) { - findMenuitems(node.firstElementChild); + var name = url.split('#')[1]; + if (typeof name === 'string') { + name = name.split('-').map(capitalize).join(' '); + } else { + name = 'Home'; } - - node = node.nextElementSibling; + return name; } } - findMenuitems(domNode.firstElementChild); + getParentMenuitem(menuitem) { + var node = menuitem.parentNode; + if (node) { + node = node.parentNode; + if (node) { + node = node.previousElementSibling; + if (node) { + if (node.getAttribute('role') === 'menuitem') { + return node; + } + } + } + } + return false; + } - return nodes; -}; + updateContent(linkURL, linkName, moveFocus) { + var h1Node, paraNodes, pathNode; -MenubarNavigation.prototype.initMenu = function (menu, depth) { - var menuitems, menuitem, role; + if (typeof moveFocus !== 'boolean') { + moveFocus = true; + } - var menuId = this.getMenuId(menu); + // Update content area + h1Node = document.querySelector('.page .main h1'); + if (h1Node) { + h1Node.textContent = linkName; + h1Node.tabIndex = -1; + if (moveFocus) { + h1Node.focus(); + } + } + paraNodes = document.querySelectorAll('.page .main p'); + paraNodes.forEach( + (p) => + (p.innerHTML = this.contentGenerator.renderParagraph(linkURL, linkName)) + ); - menuitems = this.getMenuitems(menu, depth); - this.menuOrientation[menuId] = this.getMenuOrientation(menu); + // Update aria-current + this.menuitems.forEach((item) => { + item.removeAttribute('aria-current'); + item.classList.remove('aria-current-path'); + item.title = ''; + }); - this.isPopup[menuId] = menu.getAttribute('role') === 'menu' && depth === 1; - this.isPopout[menuId] = menu.getAttribute('role') === 'menu' && depth > 1; + this.menuitems.forEach((item) => { + if (item.href === linkURL) { + item.setAttribute('aria-current', 'page'); + pathNode = this.getParentMenuitem(item); + while (pathNode) { + pathNode.classList.add('aria-current-path'); + pathNode.title = 'Contains current page link'; + pathNode = this.getParentMenuitem(pathNode); + } + } + }); + } - this.menuitemGroups[menuId] = []; - this.firstChars[menuId] = []; - this.firstMenuitem[menuId] = null; - this.lastMenuitem[menuId] = null; + getMenuitems(domNode, depth) { + var nodes = []; - for (var i = 0; i < menuitems.length; i++) { - menuitem = menuitems[i]; - role = menuitem.getAttribute('role'); + var initMenu = this.initMenu.bind(this); + var popups = this.popups; - if (role.indexOf('menuitem') < 0) { - continue; - } + function findMenuitems(node) { + var role, flag; - menuitem.tabIndex = -1; - this.menuitemGroups[menuId].push(menuitem); - this.firstChars[menuId].push(menuitem.textContent.trim().toLowerCase()[0]); + while (node) { + flag = true; + role = node.getAttribute('role'); - menuitem.addEventListener('keydown', this.handleKeydown.bind(this)); - menuitem.addEventListener('click', this.handleMenuitemClick.bind(this)); + if (role) { + role = role.trim().toLowerCase(); + } - menuitem.addEventListener( - 'mouseover', - this.handleMenuitemMouseover.bind(this) - ); + switch (role) { + case 'menu': + node.tabIndex = -1; + initMenu(node, depth + 1); + flag = false; + break; + + case 'menuitem': + if (node.getAttribute('aria-haspopup') === 'true') { + popups.push(node); + } + nodes.push(node); + break; + + default: + break; + } - if (!this.firstMenuitem[menuId]) { - if (this.hasPopup(menuitem)) { - menuitem.tabIndex = 0; + if ( + flag && + node.firstElementChild && + node.firstElementChild.tagName !== 'svg' + ) { + findMenuitems(node.firstElementChild); + } + node = node.nextElementSibling; } - this.firstMenuitem[menuId] = menuitem; } - this.lastMenuitem[menuId] = menuitem; + findMenuitems(domNode.firstElementChild); + return nodes; } -}; -MenubarNavigation.prototype.setFocusToMenuitem = function ( - menuId, - newMenuitem -) { - this.closePopupAll(newMenuitem); + initMenu(menu, depth) { + var menuitems, menuitem, role; - if (this.menuitemGroups[menuId]) { - this.menuitemGroups[menuId].forEach(function (item) { - if (item === newMenuitem) { - item.tabIndex = 0; - newMenuitem.focus(); - } else { - item.tabIndex = -1; - } - }); - } -}; + var menuId = this.getMenuId(menu); + + menuitems = this.getMenuitems(menu, depth); + this.menuOrientation[menuId] = this.getMenuOrientation(menu); -MenubarNavigation.prototype.setFocusToFirstMenuitem = function (menuId) { - this.setFocusToMenuitem(menuId, this.firstMenuitem[menuId]); -}; + this.isPopup[menuId] = menu.getAttribute('role') === 'menu' && depth === 1; + this.isPopout[menuId] = menu.getAttribute('role') === 'menu' && depth > 1; -MenubarNavigation.prototype.setFocusToLastMenuitem = function (menuId) { - this.setFocusToMenuitem(menuId, this.lastMenuitem[menuId]); -}; + this.menuitemGroups[menuId] = []; + this.firstChars[menuId] = []; + this.firstMenuitem[menuId] = null; + this.lastMenuitem[menuId] = null; -MenubarNavigation.prototype.setFocusToPreviousMenuitem = function ( - menuId, - currentMenuitem -) { - var newMenuitem, index; + for (var i = 0; i < menuitems.length; i++) { + menuitem = menuitems[i]; + role = menuitem.getAttribute('role'); - if (currentMenuitem === this.firstMenuitem[menuId]) { - newMenuitem = this.lastMenuitem[menuId]; - } else { - index = this.menuitemGroups[menuId].indexOf(currentMenuitem); - newMenuitem = this.menuitemGroups[menuId][index - 1]; + if (role.indexOf('menuitem') < 0) { + continue; + } + + menuitem.tabIndex = -1; + this.menuitems.push(menuitem); + this.menuitemGroups[menuId].push(menuitem); + this.firstChars[menuId].push( + menuitem.textContent.trim().toLowerCase()[0] + ); + + menuitem.addEventListener('keydown', this.handleKeydown.bind(this)); + menuitem.addEventListener('click', this.handleMenuitemClick.bind(this), { + capture: true, + }); + + menuitem.addEventListener( + 'mouseover', + this.handleMenuitemMouseover.bind(this) + ); + + if (!this.firstMenuitem[menuId]) { + if (this.hasPopup(menuitem)) { + menuitem.tabIndex = 0; + } + this.firstMenuitem[menuId] = menuitem; + } + this.lastMenuitem[menuId] = menuitem; + } } - this.setFocusToMenuitem(menuId, newMenuitem); + setFocusToMenuitem(menuId, newMenuitem) { + this.closePopupAll(newMenuitem); - return newMenuitem; -}; + if (this.menuitemGroups[menuId]) { + this.menuitemGroups[menuId].forEach(function (item) { + if (item === newMenuitem) { + item.tabIndex = 0; + newMenuitem.focus(); + } else { + item.tabIndex = -1; + } + }); + } + } -MenubarNavigation.prototype.setFocusToNextMenuitem = function ( - menuId, - currentMenuitem -) { - var newMenuitem, index; + setFocusToFirstMenuitem(menuId) { + this.setFocusToMenuitem(menuId, this.firstMenuitem[menuId]); + } - if (currentMenuitem === this.lastMenuitem[menuId]) { - newMenuitem = this.firstMenuitem[menuId]; - } else { - index = this.menuitemGroups[menuId].indexOf(currentMenuitem); - newMenuitem = this.menuitemGroups[menuId][index + 1]; + setFocusToLastMenuitem(menuId) { + this.setFocusToMenuitem(menuId, this.lastMenuitem[menuId]); } - this.setFocusToMenuitem(menuId, newMenuitem); - return newMenuitem; -}; + setFocusToPreviousMenuitem(menuId, currentMenuitem) { + var newMenuitem, index; -MenubarNavigation.prototype.setFocusByFirstCharacter = function ( - menuId, - currentMenuitem, - char -) { - var start, index; + if (currentMenuitem === this.firstMenuitem[menuId]) { + newMenuitem = this.lastMenuitem[menuId]; + } else { + index = this.menuitemGroups[menuId].indexOf(currentMenuitem); + newMenuitem = this.menuitemGroups[menuId][index - 1]; + } - char = char.toLowerCase(); + this.setFocusToMenuitem(menuId, newMenuitem); - // Get start index for search based on position of currentItem - start = this.menuitemGroups[menuId].indexOf(currentMenuitem) + 1; - if (start >= this.menuitemGroups[menuId].length) { - start = 0; + return newMenuitem; } - // Check remaining slots in the menu - index = this.getIndexFirstChars(menuId, start, char); + setFocusToNextMenuitem(menuId, currentMenuitem) { + var newMenuitem, index; + + if (currentMenuitem === this.lastMenuitem[menuId]) { + newMenuitem = this.firstMenuitem[menuId]; + } else { + index = this.menuitemGroups[menuId].indexOf(currentMenuitem); + newMenuitem = this.menuitemGroups[menuId][index + 1]; + } + this.setFocusToMenuitem(menuId, newMenuitem); - // If not found in remaining slots, check from beginning - if (index === -1) { - index = this.getIndexFirstChars(menuId, 0, char); + return newMenuitem; } - // If match was found... - if (index > -1) { - this.setFocusToMenuitem(menuId, this.menuitemGroups[menuId][index]); + setFocusByFirstCharacter(menuId, currentMenuitem, char) { + var start, index; + + char = char.toLowerCase(); + + // Get start index for search based on position of currentItem + start = this.menuitemGroups[menuId].indexOf(currentMenuitem) + 1; + if (start >= this.menuitemGroups[menuId].length) { + start = 0; + } + + // Check remaining slots in the menu + index = this.getIndexFirstChars(menuId, start, char); + + // If not found in remaining slots, check from beginning + if (index === -1) { + index = this.getIndexFirstChars(menuId, 0, char); + } + + // If match was found... + if (index > -1) { + this.setFocusToMenuitem(menuId, this.menuitemGroups[menuId][index]); + } } -}; -// Utilities + // Utilities -MenubarNavigation.prototype.getIndexFirstChars = function ( - menuId, - startIndex, - char -) { - for (var i = startIndex; i < this.firstChars[menuId].length; i++) { - if (char === this.firstChars[menuId][i]) { - return i; + getIndexFirstChars(menuId, startIndex, char) { + for (var i = startIndex; i < this.firstChars[menuId].length; i++) { + if (char === this.firstChars[menuId][i]) { + return i; + } } + return -1; } - return -1; -}; -MenubarNavigation.prototype.isPrintableCharacter = function (str) { - return str.length === 1 && str.match(/\S/); -}; + isPrintableCharacter(str) { + return str.length === 1 && str.match(/\S/); + } -MenubarNavigation.prototype.getIdFromAriaLabel = function (node) { - var id = node.getAttribute('aria-label'); - if (id) { - id = id.trim().toLowerCase().replace(' ', '-').replace('/', '-'); + getIdFromAriaLabel(node) { + var id = node.getAttribute('aria-label'); + if (id) { + id = id.trim().toLowerCase().replace(' ', '-').replace('/', '-'); + } + return id; } - return id; -}; -MenubarNavigation.prototype.getMenuOrientation = function (node) { - var orientation = node.getAttribute('aria-orientation'); + getMenuOrientation(node) { + var orientation = node.getAttribute('aria-orientation'); - if (!orientation) { - var role = node.getAttribute('role'); + if (!orientation) { + var role = node.getAttribute('role'); - switch (role) { - case 'menubar': - orientation = 'horizontal'; - break; + switch (role) { + case 'menubar': + orientation = 'horizontal'; + break; - case 'menu': - orientation = 'vertical'; - break; + case 'menu': + orientation = 'vertical'; + break; - default: - break; + default: + break; + } } + + return orientation; } - return orientation; -}; + getMenuId(node) { + var id = false; + var role = node.getAttribute('role'); -MenubarNavigation.prototype.getMenuId = function (node) { - var id = false; - var role = node.getAttribute('role'); + while (node && role !== 'menu' && role !== 'menubar') { + node = node.parentNode; + if (node) { + role = node.getAttribute('role'); + } + } - while (node && role !== 'menu' && role !== 'menubar') { - node = node.parentNode; if (node) { - role = node.getAttribute('role'); + id = role + '-' + this.getIdFromAriaLabel(node); } - } - if (node) { - id = role + '-' + this.getIdFromAriaLabel(node); + return id; } - return id; -}; - -MenubarNavigation.prototype.getMenu = function (menuitem) { - var menu = menuitem; - var role = menuitem.getAttribute('role'); + getMenu(menuitem) { + var menu = menuitem; + var role = menuitem.getAttribute('role'); - while (menu && role !== 'menu' && role !== 'menubar') { - menu = menu.parentNode; - if (menu) { - role = menu.getAttribute('role'); + while (menu && role !== 'menu' && role !== 'menubar') { + menu = menu.parentNode; + if (menu) { + role = menu.getAttribute('role'); + } } - } - return menu; -}; + return menu; + } -// Popup menu methods + // Popup menu methods -MenubarNavigation.prototype.isAnyPopupOpen = function () { - for (var i = 0; i < this.popups.length; i++) { - if (this.popups[i].getAttribute('aria-expanded') === 'true') { - return true; + isAnyPopupOpen() { + for (var i = 0; i < this.popups.length; i++) { + if (this.popups[i].getAttribute('aria-expanded') === 'true') { + return true; + } } + return false; } - return false; -}; -MenubarNavigation.prototype.openPopup = function (menuId, menuitem) { - // set aria-expanded attribute - var popupMenu = menuitem.nextElementSibling; - - var rect = menuitem.getBoundingClientRect(); - - // Set CSS properties - if (this.isPopup[menuId]) { - popupMenu.parentNode.style.position = 'relative'; - popupMenu.style.display = 'block'; - popupMenu.style.position = 'absolute'; - popupMenu.style.left = rect.width + 6 + 'px'; - popupMenu.style.top = '0px'; - popupMenu.style.zIndex = 100; - } else { - popupMenu.style.display = 'block'; - popupMenu.style.position = 'absolute'; - popupMenu.style.left = '0px'; - popupMenu.style.top = rect.height + 8 + 'px'; - popupMenu.style.zIndex = 100; + setMenubarDataExpanded(value) { + this.domNode.setAttribute('data-menubar-item-expanded', value); } - menuitem.setAttribute('aria-expanded', 'true'); + isMenubarDataExpandedTrue() { + return this.domNode.getAttribute('data-menubar-item-expanded') === 'true'; + } - return this.getMenuId(popupMenu); -}; + openPopup(menuId, menuitem) { + // set aria-expanded attribute + var popupMenu = menuitem.nextElementSibling; + + if (popupMenu) { + var rect = menuitem.getBoundingClientRect(); + + // Set CSS properties + if (this.isPopup[menuId]) { + popupMenu.parentNode.style.position = 'relative'; + popupMenu.style.display = 'block'; + popupMenu.style.position = 'absolute'; + popupMenu.style.left = rect.width + 10 + 'px'; + popupMenu.style.top = '0px'; + popupMenu.style.zIndex = 100; + } else { + popupMenu.style.display = 'block'; + popupMenu.style.position = 'absolute'; + popupMenu.style.left = '0px'; + popupMenu.style.top = rect.height + 8 + 'px'; + popupMenu.style.zIndex = 100; + } -MenubarNavigation.prototype.closePopout = function (menuitem) { - var menu, - menuId = this.getMenuId(menuitem), - cmi = menuitem; + menuitem.setAttribute('aria-expanded', 'true'); + this.setMenubarDataExpanded('true'); + return this.getMenuId(popupMenu); + } - while (this.isPopup[menuId] || this.isPopout[menuId]) { - menu = this.getMenu(cmi); - cmi = menu.previousElementSibling; - menuId = this.getMenuId(cmi); - cmi.setAttribute('aria-expanded', 'false'); - menu.style.display = 'none'; + return false; } - cmi.focus(); - return cmi; -}; -MenubarNavigation.prototype.closePopup = function (menuitem) { - var menu, - menuId = this.getMenuId(menuitem), - cmi = menuitem; + closePopout(menuitem) { + var menu, + menuId = this.getMenuId(menuitem), + cmi = menuitem; - if (this.isMenubar(menuId)) { - if (this.isOpen(menuitem)) { - menuitem.setAttribute('aria-expanded', 'false'); - menuitem.nextElementSibling.style.display = 'none'; + while (this.isPopup[menuId] || this.isPopout[menuId]) { + menu = this.getMenu(cmi); + cmi = menu.previousElementSibling; + menuId = this.getMenuId(cmi); + menu.style.display = 'none'; } - } else { - menu = this.getMenu(menuitem); - cmi = menu.previousElementSibling; - cmi.setAttribute('aria-expanded', 'false'); cmi.focus(); - menu.style.display = 'none'; + return cmi; } - return cmi; -}; + closePopup(menuitem) { + var menu, + menuId = this.getMenuId(menuitem), + cmi = menuitem; + + if (this.isMenubar(menuId)) { + if (this.isOpen(menuitem)) { + menuitem.setAttribute('aria-expanded', 'false'); + menuitem.nextElementSibling.style.display = 'none'; + } + } else { + menu = this.getMenu(menuitem); + cmi = menu.previousElementSibling; + cmi.setAttribute('aria-expanded', 'false'); + cmi.focus(); + menu.style.display = 'none'; + } -MenubarNavigation.prototype.doesNotContain = function (popup, menuitem) { - if (menuitem) { - return !popup.nextElementSibling.contains(menuitem); + return cmi; } - return true; -}; -MenubarNavigation.prototype.closePopupAll = function (menuitem) { - if (typeof menuitem !== 'object') { - menuitem = false; + doesNotContain(popup, menuitem) { + if (menuitem) { + return !popup.nextElementSibling.contains(menuitem); + } + return true; } - for (var i = 0; i < this.popups.length; i++) { - var popup = this.popups[i]; - if (this.doesNotContain(popup, menuitem) && this.isOpen(popup)) { - var cmi = popup.nextElementSibling; - if (cmi) { - popup.setAttribute('aria-expanded', 'false'); - cmi.style.display = 'none'; + + closePopupAll(menuitem) { + if (typeof menuitem !== 'object') { + menuitem = false; + } + for (var i = 0; i < this.popups.length; i++) { + var popup = this.popups[i]; + if (this.doesNotContain(popup, menuitem) && this.isOpen(popup)) { + var cmi = popup.nextElementSibling; + if (cmi) { + popup.setAttribute('aria-expanded', 'false'); + cmi.style.display = 'none'; + } } } } -}; - -MenubarNavigation.prototype.hasPopup = function (menuitem) { - return menuitem.getAttribute('aria-haspopup') === 'true'; -}; -MenubarNavigation.prototype.isOpen = function (menuitem) { - return menuitem.getAttribute('aria-expanded') === 'true'; -}; - -MenubarNavigation.prototype.isMenubar = function (menuId) { - return !this.isPopup[menuId] && !this.isPopout[menuId]; -}; - -MenubarNavigation.prototype.isMenuHorizontal = function (menuitem) { - return this.menuOrientation[menuitem] === 'horizontal'; -}; + hasPopup(menuitem) { + return menuitem.getAttribute('aria-haspopup') === 'true'; + } -MenubarNavigation.prototype.hasFocus = function () { - return this.domNode.classList.contains('focus'); -}; + isOpen(menuitem) { + return menuitem.getAttribute('aria-expanded') === 'true'; + } -// Menu event handlers + isMenubar(menuId) { + return !this.isPopup[menuId] && !this.isPopout[menuId]; + } -MenubarNavigation.prototype.handleMenubarFocusin = function () { - // if the menubar or any of its menus has focus, add styling hook for hover - this.domNode.classList.add('focus'); -}; + isMenuHorizontal(menuitem) { + return this.menuOrientation[menuitem] === 'horizontal'; + } -MenubarNavigation.prototype.handleMenubarFocusout = function () { - // remove styling hook for hover on menubar item - this.domNode.classList.remove('focus'); -}; + hasFocus() { + return this.domNode.classList.contains('focus'); + } -MenubarNavigation.prototype.handleKeydown = function (event) { - var tgt = event.currentTarget, - key = event.key, - flag = false, - menuId = this.getMenuId(tgt), - id, - popupMenuId, - mi; + // Menu event handlers - var isAnyPopupOpen = this.isAnyPopupOpen(); + handleMenubarFocusin() { + // if the menubar or any of its menus has focus, add styling hook for hover + this.domNode.classList.add('focus'); + } - switch (key) { - case ' ': - case 'Enter': - if (this.hasPopup(tgt)) { - this.openPopups = true; - popupMenuId = this.openPopup(menuId, tgt); - this.setFocusToFirstMenuitem(popupMenuId); - } else { - if (tgt.href !== '#') { - this.closePopupAll(); - window.location.href = tgt.href; - } - } - flag = true; - break; - - case 'Esc': - case 'Escape': - this.openPopups = false; - this.closePopup(tgt); - flag = true; - break; - - case 'Up': - case 'ArrowUp': - if (this.isMenuHorizontal(menuId)) { - if (this.hasPopup(tgt)) { - this.openPopups = true; - popupMenuId = this.openPopup(menuId, tgt); - this.setFocusToLastMenuitem(popupMenuId); - } - } else { - this.setFocusToPreviousMenuitem(menuId, tgt); - } - flag = true; - break; + handleMenubarFocusout() { + // remove styling hook for hover on menubar item + this.domNode.classList.remove('focus'); + } - case 'ArrowDown': - case 'Down': - if (this.isMenuHorizontal(menuId)) { + handleKeydown(event) { + var tgt = event.currentTarget, + key = event.key, + flag = false, + menuId = this.getMenuId(tgt), + id, + popupMenuId, + mi; + + switch (key) { + case ' ': + case 'Enter': if (this.hasPopup(tgt)) { this.openPopups = true; popupMenuId = this.openPopup(menuId, tgt); this.setFocusToFirstMenuitem(popupMenuId); + } else { + if (tgt.href !== '#') { + this.closePopupAll(); + this.updateContent(tgt.href, tgt.textContent.trim()); + this.setMenubarDataExpanded('false'); + } } - } else { - this.setFocusToNextMenuitem(menuId, tgt); - } - flag = true; - break; - - case 'Left': - case 'ArrowLeft': - if (this.isMenuHorizontal(menuId)) { - mi = this.setFocusToPreviousMenuitem(menuId, tgt); - if (isAnyPopupOpen) { - this.openPopup(menuId, mi); + flag = true; + break; + + case 'Esc': + case 'Escape': + this.openPopups = false; + mi = this.closePopup(tgt); + id = this.getMenuId(mi); + this.setMenubarDataExpanded('false'); + flag = true; + break; + + case 'Up': + case 'ArrowUp': + if (this.isMenuHorizontal(menuId)) { + if (this.hasPopup(tgt)) { + this.openPopups = true; + popupMenuId = this.openPopup(menuId, tgt); + this.setFocusToLastMenuitem(popupMenuId); + } + } else { + this.setFocusToPreviousMenuitem(menuId, tgt); } - } else { - if (this.isPopout[menuId]) { - mi = this.closePopup(tgt); - id = this.getMenuId(mi); - mi = this.setFocusToMenuitem(id, mi); + flag = true; + break; + + case 'ArrowDown': + case 'Down': + if (this.isMenuHorizontal(menuId)) { + if (this.hasPopup(tgt)) { + this.openPopups = true; + popupMenuId = this.openPopup(menuId, tgt); + this.setFocusToFirstMenuitem(popupMenuId); + } } else { - mi = this.closePopup(tgt); - id = this.getMenuId(mi); - mi = this.setFocusToPreviousMenuitem(id, mi); - this.openPopup(id, mi); + this.setFocusToNextMenuitem(menuId, tgt); } - } - flag = true; - break; - - case 'Right': - case 'ArrowRight': - if (this.isMenuHorizontal(menuId)) { - mi = this.setFocusToNextMenuitem(menuId, tgt); - if (isAnyPopupOpen) { - this.openPopup(menuId, mi); + flag = true; + break; + + case 'Left': + case 'ArrowLeft': + if (this.isMenuHorizontal(menuId)) { + mi = this.setFocusToPreviousMenuitem(menuId, tgt); + if (this.isAnyPopupOpen() || this.isMenubarDataExpandedTrue()) { + this.openPopup(menuId, mi); + } + } else { + if (this.isPopout[menuId]) { + mi = this.closePopup(tgt); + id = this.getMenuId(mi); + mi = this.setFocusToMenuitem(id, mi); + } else { + mi = this.closePopup(tgt); + id = this.getMenuId(mi); + mi = this.setFocusToPreviousMenuitem(id, mi); + this.openPopup(id, mi); + } } - } else { - if (this.hasPopup(tgt)) { - popupMenuId = this.openPopup(menuId, tgt); - this.setFocusToFirstMenuitem(popupMenuId); + flag = true; + break; + + case 'Right': + case 'ArrowRight': + if (this.isMenuHorizontal(menuId)) { + mi = this.setFocusToNextMenuitem(menuId, tgt); + if (this.isAnyPopupOpen() || this.isMenubarDataExpandedTrue()) { + this.openPopup(menuId, mi); + } } else { - mi = this.closePopout(tgt); - id = this.getMenuId(mi); - mi = this.setFocusToNextMenuitem(id, mi); - this.openPopup(id, mi); + if (this.hasPopup(tgt)) { + popupMenuId = this.openPopup(menuId, tgt); + this.setFocusToFirstMenuitem(popupMenuId); + } else { + mi = this.closePopout(tgt); + id = this.getMenuId(mi); + mi = this.setFocusToNextMenuitem(id, mi); + this.openPopup(id, mi); + } } - } - flag = true; - break; - - case 'Home': - case 'PageUp': - this.setFocusToFirstMenuitem(menuId, tgt); - flag = true; - break; - - case 'End': - case 'PageDown': - this.setFocusToLastMenuitem(menuId, tgt); - flag = true; - break; - - case 'Tab': - this.openPopups = false; - this.closePopup(tgt); - break; - - default: - if (this.isPrintableCharacter(key)) { - this.setFocusByFirstCharacter(menuId, tgt, key); flag = true; - } - break; - } + break; - if (flag) { - event.stopPropagation(); - event.preventDefault(); + case 'Home': + case 'PageUp': + this.setFocusToFirstMenuitem(menuId, tgt); + flag = true; + break; + + case 'End': + case 'PageDown': + this.setFocusToLastMenuitem(menuId, tgt); + flag = true; + break; + + case 'Tab': + this.openPopups = false; + this.setMenubarDataExpanded('false'); + this.closePopup(tgt); + break; + + default: + if (this.isPrintableCharacter(key)) { + this.setFocusByFirstCharacter(menuId, tgt, key); + flag = true; + } + break; + } + + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } } -}; -MenubarNavigation.prototype.handleMenuitemClick = function (event) { - var tgt = event.currentTarget; - var menuId = this.getMenuId(tgt); + handleMenuitemClick(event) { + var tgt = event.currentTarget; + var menuId = this.getMenuId(tgt); - if (this.hasPopup(tgt)) { - if (this.isOpen(tgt)) { - this.closePopup(tgt); + if (this.hasPopup(tgt)) { + if (this.isOpen(tgt)) { + this.closePopup(tgt); + } else { + this.closePopupAll(tgt); + this.openPopup(menuId, tgt); + } } else { - this.closePopupAll(tgt); - this.openPopup(menuId, tgt); + this.updateContent(tgt.href, tgt.textContent.trim()); + this.closePopupAll(); } event.stopPropagation(); event.preventDefault(); } -}; -MenubarNavigation.prototype.handleMenuitemMouseover = function (event) { - var tgt = event.currentTarget; - var menuId = this.getMenuId(tgt); + handleMenuitemMouseover(event) { + var tgt = event.currentTarget; + var menuId = this.getMenuId(tgt); - if (this.hasFocus()) { - this.setFocusToMenuitem(menuId, tgt); - } + if (this.hasFocus()) { + this.setFocusToMenuitem(menuId, tgt); + } - if (this.isAnyPopupOpen() || this.hasFocus()) { - this.closePopupAll(tgt); - if (this.hasPopup(tgt)) { - this.openPopup(menuId, tgt); + if (this.isAnyPopupOpen() || this.hasFocus()) { + this.closePopupAll(tgt); + if (this.hasPopup(tgt)) { + this.openPopup(menuId, tgt); + } } } -}; -MenubarNavigation.prototype.handleBackgroundMousedown = function (event) { - if (!this.domNode.contains(event.target)) { - this.closePopupAll(); + handleBackgroundMousedown(event) { + if (!this.domNode.contains(event.target)) { + this.closePopupAll(); + } } -}; +} // Initialize menubar editor diff --git a/examples/menubar/mb-about.html b/examples/menubar/mb-about.html deleted file mode 100644 index d197d5f9e2..0000000000 --- a/examples/menubar/mb-about.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - Menubar Example Landing Page: About - - - - - -
    -

    Menubar Example Landing Page

    -
    -
    -

    About

    -

    Back to menubar example

    - -

    Overview

    -

    Back to menubar example

    - -

    Administration

    -

    Back to menubar example

    - -

    Facts

    -

    Back to menubar example

    - -

    Campus Tours

    -

    Back to menubar example

    - -
    - - - diff --git a/examples/menubar/mb-academics.html b/examples/menubar/mb-academics.html deleted file mode 100644 index 08b3d146a1..0000000000 --- a/examples/menubar/mb-academics.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - Menubar Example Landing Page: Academics - - - - - -
    -

    Menubar Example Landing Page

    -
    - -
    -

    Academics

    -

    Back to menubar example

    - -

    Colleges & Schools

    -

    Back to menubar example

    - - -

    Programs of Study

    -

    Back to menubar example

    - - -

    Honors Programs

    -

    Back to menubar example

    - - -

    Online Courses

    -

    Back to menubar example

    - - -

    Course Explorer

    -

    Back to menubar example

    - - -

    Register for Class

    -

    Back to menubar example

    - - -

    Academic Calendar

    -

    Back to menubar example

    - - -

    Transcripts

    -

    Back to menubar example

    - -
    - - - diff --git a/examples/menubar/mb-admissions.html b/examples/menubar/mb-admissions.html deleted file mode 100644 index 4f0d4efff2..0000000000 --- a/examples/menubar/mb-admissions.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - Menubar Example Landing Page: Admissions - - - - - -
    -

    Menubar Example Landing Page

    -
    -
    -

    Admissions

    -

    Back to menubar example

    - -

    Apply

    -

    Back to menubar example

    - -

    Tuition

    -

    Back to menubar example

    - -

    Sign Up

    -

    Back to menubar example

    - -

    Visit

    -

    Back to menubar example

    - -

    Photo Tour

    -

    Back to menubar example

    - -

    Connect

    -

    Back to menubar example

    - - -
    - - - diff --git a/examples/menubar/menubar-navigation.html b/examples/menubar/menubar-navigation.html index 215b7dc552..c18d0f249b 100644 --- a/examples/menubar/menubar-navigation.html +++ b/examples/menubar/menubar-navigation.html @@ -29,11 +29,23 @@

    Navigation Menubar Example

    +
    +

    CAUTION! Before considering use of the ARIA menubar pattern for site navigation, it is important to understand:

    + +

    The following implementation of the design pattern for menubar - demonstrates a menubar that provides site navigation menus. - Each item in the menubar represents a section of a web site for a mythical university and opens a submenu containing menu items that link to pages within that section. + demonstrates how a menubar can provide navigation menus. + The parent menu items in the menubar represent a section of a mythical university web site and open a submenu containing menu items that link to pages within that section. + The navigation system it illustrates is comparable to the navigation illustrated in the Example of Disclosure for Navigation Menus. + As noted above, the disclosure pattern is better suited for most web sites because few sites need the additional keyboard functionality required to support the ARIA menubar and menu roles.

    Similar examples include: