From 34fb7626f9ae9992ae4eb9ff73e4bbfa14db982b Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Mon, 9 Nov 2020 17:18:48 -0600 Subject: [PATCH 01/15] skipto example --- examples/carousel/carousel-2-tablist.html | 11 +- examples/js/skipto.js | 1292 +++++++++++++++++++++ 2 files changed, 1302 insertions(+), 1 deletion(-) create mode 100644 examples/js/skipto.js diff --git a/examples/carousel/carousel-2-tablist.html b/examples/carousel/carousel-2-tablist.html index 5b1741196b..08137bb7fa 100644 --- a/examples/carousel/carousel-2-tablist.html +++ b/examples/carousel/carousel-2-tablist.html @@ -11,7 +11,16 @@ - + + diff --git a/examples/js/skipto.js b/examples/js/skipto.js new file mode 100644 index 0000000000..434196bb54 --- /dev/null +++ b/examples/js/skipto.js @@ -0,0 +1,1292 @@ +/*! skipto - v3.1.0 - 2020-11-09 + * https://github.com/paypal/skipto + * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ +/*@cc_on @*/ +/*@if (@_jscript_version >= 5.8) @*/ +/*jslint devel: true */ +/* ======================================================================== + * Copyright (c) <2020> PayPal + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of PayPal or any of its subsidiaries or affiliates nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ======================================================================== */ +'use strict'; +(function () { + var SkipTo = { + domNode: null, + buttonNode: null, + menuNode: null, + menuitemNodes: [], + firstMenuitem: false, + lastMenuitem: false, + firstChars: [], + headingLevels: [], + skipToIdIndex: 1, + showAllLandmarksSelector: + 'main, [role=main], [role=search], nav, [role=navigation], section[aria-label], section[aria-labelledby], section[title], [role=region][aria-label], [role=region][aria-labelledby], [role=region][title], form[aria-label], form[aria-labelledby], aside, [role=complementary], body > header, [role=banner], body > footer, [role=contentinfo]', + showAllHeadingsSelector: 'h1, h2, h3, h4, h5, h6', + // Default configuration values + config: { + // Feature switches + enableActions: true, + enableHeadingLevelShortcuts: true, + enableHelp: true, + // Customization of button and menu + accesskey: '0', // default is the number zero + attachElement: 'header', + displayOption: 'static', // options: static (default), popup + // container element, use containerClass for custom styling + containerElement: 'div', + containerRole: '', + customClass: '', + + // Button labels and messages + accesskeyNotSupported: ' is not supported on this browser.', + buttonTitle: 'Keyboard Navigation', + buttonTitleWithAccesskey: 'Keyboard Navigation\nAccesskey is "$key"', + buttonLabel: 'Skip To Content', + + // Menu labels and messages + menuLabel: 'Landmarks and Headings', + landmarkImportantGroupLabel: 'Important Landmarks', + landmarkAllGroupLabel: 'All Landmarks', + headingImportantGroupLabel: 'Important Headings', + headingAllGroupLabel: 'All Headings', + mainLabel: 'main', + searchLabel: 'search', + navLabel: 'menu', + asideLabel: 'aside', + footerLabel: 'footer', + headerLabel: 'header', + formLabel: 'form', + msgNoLandmarksFound: 'No landmarks found', + msgNoHeadingsFound: 'No headings found', + + // Action labels and messages + actionGroupLabel: 'Actions', + actionShowHeadingsHelp: + 'Toggles between showing "All" and "Important" headings.', + actionShowImportantHeadingsLabel: 'Show Important Headings ($num)', + actionShowAllHeadingsLabel: 'Show All headings ($num)', + actionShowLandmarksHelp: + 'Toggles between showing "All" and "Important" landmarks.', + actionShowImportantLandmarksLabel: 'Show Important landmarks ($num)', + actionShowAllLandmarksLabel: 'Show All landmarks ($num)', + + // Selectors for landmark and headings sections + landmarks: + 'main, [role="main"], [role="search"], nav, [role="navigation"], aside, [role="complementary"]', + headings: 'main h1, [role="main"] h1, main h2, [role="main"] h2', + + // Custom CSS position and colors + colorTheme: '', + positionLeft: '', + buttonColor: '', + buttonBackgroundColor: '', + buttonBorderColor: '', + buttonColorFocus: '', + buttonFocusBackgroundColor: '', + buttonFocusBorderColor: '', + menuBackgroundColor: '', + menuitemColor: '', + menuitemBackgroundColor: '', + menuitemFocusColor: '', + menuitemFocusBackgroundColor: '', + menuitemFocusBorderColor: '', + }, + colorThemes: { + default: { + positionLeft: '46%', + buttonColor: '#1a1a1a', + buttonBackgroundColor: '#eeeeee', + buttonBorderColor: '#eeeeee', + buttonFocusColor: '#000000', + buttonFocusBackgroundColor: '#dcdcdc', + buttonFocusBorderColor: '#1a1a1a', + menuBackgroundColor: '#eeeeee', + menuBorderColor: '1a1a1a', + menuitemColor: '#1a1a1a', + menuitemBackgroundColor: '#eeeeee', + menuitemFocusColor: '#eeeeee', + menuitemFocusBackgroundColor: '#1a1a1a', + menuitemFocusBorderColor: '#1a1a1a', + }, + illinois: { + positionLeft: '46%', + buttonColor: '#00132c', + buttonBackgroundColor: '#dddede', + buttonBorderColor: '#dddede', + buttonFocusColor: '#00132c', + buttonFocusBackgroundColor: '#cad9ef', + buttonFocusBorderColor: '#ff552e', + menuBackgroundColor: '#cad9ef', + menuitemLevelOpacity: '0.7', + menuBorderColor: '#ff552e', + menuitemColor: '#00132c', + menuitemBackgroundColor: '#cad9ef', + menuitemFocusColor: '#eeeeee', + menuitemFocusBackgroundColor: '#00132c', + menuitemFocusBorderColor: '#ff552e', + }, + aria: { + positionLeft: '380px', + buttonColor: '#005a9c;', + buttonBackgroundColor: '#def', + buttonBorderColor: '#def', + buttonFocusColor: '#fff', + buttonFocusBackgroundColor: '#005a9c', + buttonFocusBorderColor: '#005a9c;', + menuBackgroundColor: '#def', + menuBorderColor: '#005a9c', + menuitemColor: '#000', + menuitemBackgroundColor: '#def', + menuitemFocusColor: '#fff', + menuitemFocusBackgroundColor: '#005a9c', + menuitemFocusBorderColor: '#005a9c', + }, + }, + defaultCSS: + '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].landmark .label,.skip-to [role=menuitem].noitems .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', + + // + // Functions related to configuring the features + // of skipTo + // + + updateStyle: function (stylePlaceholder, value, defaultValue) { + if (typeof value !== 'string' || value.length === 0) { + value = defaultValue; + } + var index1 = this.defaultCSS.indexOf(stylePlaceholder); + var index2 = index1 + stylePlaceholder.length; + while (index1 >= 0 && index2 < this.defaultCSS.length) { + this.defaultCSS = + this.defaultCSS.substring(0, index1) + + value + + this.defaultCSS.substring(index2); + index1 = this.defaultCSS.indexOf(stylePlaceholder, index2); + index2 = index1 + stylePlaceholder.length; + } + }, + addCSSColors: function () { + var theme = this.colorThemes['default']; + if (typeof this.colorThemes[this.config.colorTheme] === 'object') { + theme = this.colorThemes[this.config.colorTheme]; + } + this.updateStyle( + '$positionLeft', + this.config.positionLeft, + theme.positionLeft + ); + this.updateStyle( + '$buttonColor', + this.config.buttonColor, + theme.buttonColor + ); + this.updateStyle( + '$buttonBackgroundColor', + this.config.buttonBackgroundColor, + theme.buttonBackgroundColor + ); + this.updateStyle( + '$buttonBorderColor', + this.config.buttonBorderColor, + theme.buttonBorderColor + ); + this.updateStyle( + '$buttonFocusColor', + this.config.buttonFocusColor, + theme.buttonFocusColor + ); + this.updateStyle( + '$buttonFocusBackgroundColor', + this.config.buttonFocusBackgroundColor, + theme.buttonFocusBackgroundColor + ); + this.updateStyle( + '$buttonFocusBorderColor', + this.config.buttonFocusBorderColor, + theme.buttonFocusBorderColor + ); + this.updateStyle( + '$menuBackgroundColor', + this.config.menuBackgroundColor, + theme.menuBackgroundColor + ); + this.updateStyle( + '$menuBorderColor', + this.config.menuBorderColor, + theme.menuBorderColor + ); + this.updateStyle( + '$menuitemColor', + this.config.menuitemColor, + theme.menuitemColor + ); + this.updateStyle( + '$menuitemBackgroundColor', + this.config.menuitemBackgroundColor, + theme.menuitemBackgroundColor + ); + this.updateStyle( + '$menuitemFocusColor', + this.config.menuitemFocusColor, + theme.menuitemFocusColor + ); + this.updateStyle( + '$menuitemFocusBackgroundColor', + this.config.menuitemFocusBackgroundColor, + theme.menuitemFocusBackgroundColor + ); + this.updateStyle( + '$menuitemFocusBorderColor', + this.config.menuitemFocusBorderColor, + theme.menuitemFocusBorderColor + ); + }, + isNotEmptyString: function (str) { + return typeof str === 'string' && str.length; + }, + getBrowserSpecificAccesskey: function (accesskey) { + var userAgent = navigator.userAgent.toLowerCase(); + var platform = navigator.platform.toLowerCase(); + + var hasWin = platform.indexOf('win') >= 0; + var hasMac = platform.indexOf('mac') >= 0; + var hasLinux = + platform.indexOf('linux') >= 0 || platform.indexOf('bsd') >= 0; + + var hasFirefox = userAgent.indexOf('firefox') >= 0; + var hasChrome = userAgent.indexOf('chrome') >= 0; + var hasOpera = userAgent.indexOf('opr') >= 0; + + if (hasWin || hasLinux) { + if (hasFirefox) { + return 'Shift+Alt+' + accesskey; + } else { + if (hasChrome || hasOpera) { + return 'Alt+' + accesskey; + } + } + } + + if (hasMac) { + return 'Control+Option+' + accesskey; + } + + return accesskey + this.config.accesskeyNotSupported; + }, + init: function (config) { + var attachElement = document.body; + if (config) { + this.setUpConfig(config); + } + if (typeof this.config.attachElement === 'string') { + var node = document.querySelector(this.config.attachElement); + if (node && node.nodeType === Node.ELEMENT_NODE) { + attachElement = node; + } + } + this.addCSSColors(); + this.addStyleElement(this.defaultCSS); + this.domNode = document.createElement(this.config.containerElement); + this.domNode.classList.add('skip-to'); + if (this.isNotEmptyString(this.config.customClass)) { + this.domNode.classList.add(this.config.customClass); + } + if (this.isNotEmptyString(this.config.containerRole)) { + this.domNode.setAttribute('role', this.config.containerRole); + } + var displayOption = this.config.displayOption; + if (typeof displayOption === 'string') { + displayOption = displayOption.trim().toLowerCase(); + if (displayOption.length) { + switch (this.config.displayOption) { + case 'onfocus': // Legacy option + case 'popup': + this.domNode.classList.add('popup'); + break; + default: + break; + } + } + } + // Place skip to at the beginning of the document + if (attachElement.firstElementChild) { + attachElement.insertBefore( + this.domNode, + attachElement.firstElementChild + ); + } else { + attachElement.appendChild(this.domNode); + } + this.buttonNode = document.createElement('button'); + this.buttonNode.textContent = this.config.buttonLabel; + this.buttonNode.setAttribute('aria-haspopup', 'true'); + this.buttonNode.setAttribute('aria-expanded', 'false'); + this.buttonNode.setAttribute('accesskey', this.config.accesskey); + + if ( + this.isNotEmptyString(this.config.buttonTitleWithAccesskey) && + this.config.accesskey.length === 1 + ) { + var title = this.config.buttonTitleWithAccesskey.replace( + '$key', + this.getBrowserSpecificAccesskey(this.config.accesskey) + ); + this.buttonNode.setAttribute('title', title); + } else { + if (this.isNotEmptyString(this.config.buttonTitle)) { + this.buttonNode.setAttribute('title', this.config.buttonTitle); + } + } + + this.domNode.appendChild(this.buttonNode); + this.menuNode = document.createElement('div'); + this.menuNode.setAttribute('role', 'menu'); + this.domNode.appendChild(this.menuNode); + this.buttonNode.addEventListener( + 'keydown', + this.handleButtonKeydown.bind(this) + ); + this.buttonNode.addEventListener( + 'click', + this.handleButtonClick.bind(this) + ); + this.domNode.addEventListener('focusin', this.handleFocusin.bind(this)); + this.domNode.addEventListener('focusout', this.handleFocusout.bind(this)); + window.addEventListener( + 'mousedown', + this.handleBackgroundMousedown.bind(this), + true + ); + }, + setUpConfig: function (appConfig) { + var localConfig = this.config, + name, + appConfigSettings = + typeof appConfig.settings !== 'undefined' + ? appConfig.settings.skipTo + : {}; + for (name in appConfigSettings) { + //overwrite values of our local config, based on the external config + if ( + typeof this.config[name] !== 'undefined' && + ((typeof appConfigSettings[name] === 'string' && + appConfigSettings[name].length > 0) || + typeof appConfigSettings[name] === 'boolean') + ) { + localConfig[name] = appConfigSettings[name]; + } else { + console.log( + '** SkipTo Problem with user configuration option "' + name + '".' + ); + } + } + }, + addStyleElement: function (cssString) { + var styleNode = document.createElement('style'); + var headNode = document.getElementsByTagName('head')[0]; + var css = document.createTextNode(cssString); + + styleNode.setAttribute('type', 'text/css'); + styleNode.appendChild(css); + headNode.appendChild(styleNode); + }, + + // + // Functions related to creating and populating the + // the popup menu + // + + getFirstChar: function (menuitem) { + var c = ''; + var label = menuitem.querySelector('.label'); + if (label && label.textContent.length) { + c = label.textContent.trim()[0].toLowerCase(); + } + return c; + }, + + getHeadingLevelFromAttribute: function (menuitem) { + var level = ''; + if (menuitem.hasAttribute('data-level')) { + level = menuitem.getAttribute('data-level'); + } + return level; + }, + + updateKeyboardShortCuts: function () { + var mi; + this.firstChars = []; + this.headingLevels = []; + + for (var i = 0; i < this.menuitemNodes.length; i += 1) { + mi = this.menuitemNodes[i]; + this.firstChars.push(this.getFirstChar(mi)); + this.headingLevels.push(this.getHeadingLevelFromAttribute(mi)); + } + }, + + updateMenuitems: function () { + var menuitemNodes = this.menuNode.querySelectorAll('[role=menuitem'); + + this.menuitemNodes = []; + for (var i = 0; i < menuitemNodes.length; i += 1) { + this.menuitemNodes.push(menuitemNodes[i]); + } + + this.firstMenuitem = this.menuitemNodes[0]; + this.lastMenuitem = this.menuitemNodes[this.menuitemNodes.length - 1]; + this.lastMenuitem.classList.add('last'); + this.updateKeyboardShortCuts(); + }, + + addMenuitemToGroup: function (groupNode, mi) { + var tagNode, tagNodeChild, labelNode; + + var menuitemNode = document.createElement('div'); + menuitemNode.setAttribute('role', 'menuitem'); + menuitemNode.classList.add(mi.class); + menuitemNode.setAttribute('data-id', mi.dataId); + menuitemNode.tabIndex = -1; + + // add event handlers + menuitemNode.addEventListener( + 'keydown', + this.handleMenuitemKeydown.bind(this) + ); + menuitemNode.addEventListener( + 'click', + this.handleMenuitemClick.bind(this) + ); + menuitemNode.addEventListener( + 'mouseover', + this.handleMenuitemMouseover.bind(this) + ); + + groupNode.appendChild(menuitemNode); + + // add heading level and label + if (mi.class.includes('heading')) { + if (this.config.enableHeadingLevelShortcuts) { + tagNode = document.createElement('span'); + tagNodeChild = document.createElement('span'); + tagNodeChild.appendChild( + document.createTextNode(mi.tagName.substring(1)) + ); + tagNode.append(tagNodeChild); + tagNode.appendChild(document.createTextNode(')')); + tagNode.classList.add('level'); + menuitemNode.append(tagNode); + } else { + menuitemNode.classList.add('no-level'); + } + menuitemNode.setAttribute('data-level', mi.tagName.substring(1)); + if (mi.tagName && mi.tagName.length) { + menuitemNode.classList.add('skip-to-' + mi.tagName); + } + } + + labelNode = document.createElement('span'); + labelNode.appendChild(document.createTextNode(mi.name)); + labelNode.classList.add('label'); + menuitemNode.append(labelNode); + + return menuitemNode; + }, + + addMenuitemGroup: function (groupId, title) { + var labelNode, groupNode; + var menuNode = this.menuNode; + if (title) { + labelNode = document.createElement('div'); + labelNode.id = groupId + '-label'; + labelNode.setAttribute('role', 'separator'); + labelNode.textContent = title; + menuNode.appendChild(labelNode); + groupNode = document.createElement('div'); + groupNode.setAttribute('role', 'group'); + groupNode.setAttribute('aria-labelledby', labelNode.id); + groupNode.id = groupId; + menuNode.appendChild(groupNode); + menuNode = groupNode; + } + return groupNode; + }, + + addMenuitemsToGroup: function (groupNode, menuitems, msgNoItemsFound) { + groupNode.innerHTML = ''; + + if (menuitems.length === 0) { + var item = {}; + item.name = msgNoItemsFound; + item.tagName = 'notag'; + item.class = 'noitems'; + item.dataId = ''; + this.addMenuitemToGroup(groupNode, item); + } else { + for (var i = 0; i < menuitems.length; i += 1) { + this.addMenuitemToGroup(groupNode, menuitems[i]); + } + } + }, + + getHeadingsGroupLabel: function (option) { + if (option === 'all') { + return this.config.headingAllGroupLabel; + } + return this.config.headingImportantGroupLabel; + }, + + getShowMoreHeadingsSelector: function (option) { + if (option === 'all') { + return this.showAllHeadingsSelector; + } + return this.config.headings; + }, + + getShowMoreHeadingsLabel: function (option) { + var label, n; + + label = this.config.actionShowImportantHeadingsLabel; + + if (option === 'all') { + label = this.config.actionShowAllHeadingsLabel; + } + n = this.getHeadings(this.getShowMoreHeadingsSelector(option)); + if (n && n.length) { + n = n.length; + } else { + n = '0'; + } + + return label.replace('$num', n); + }, + + addActionMoreHeadings: function (groupNode) { + var item = {}; + item.name = this.getShowMoreHeadingsLabel('all'); + item.tagName = 'action'; + item.role = 'menuitem'; + item.class = 'action'; + item.dataId = 'skip-to-more-headings'; + var menuitemNode = this.addMenuitemToGroup(groupNode, item); + menuitemNode.setAttribute('data-show-heading-option', 'all'); + menuitemNode.title = this.config.actionShowHeadingsHelp; + }, + + updateHeadingGroupMenuitems: function (option) { + var selector = this.getShowMoreHeadingsSelector(option); + var headings = this.getHeadings(selector); + var groupNode = document.getElementById('id-skip-to-group-headings'); + this.addMenuitemsToGroup( + groupNode, + headings, + this.config.msgNoHeadingsFound + ); + this.updateMenuitems(); + + // Move focus to first heading menuitem + if (groupNode.firstElementChild) { + groupNode.firstElementChild.focus(); + } + + var labelNode = this.menuNode.querySelector( + '#id-skip-to-group-headings-label' + ); + labelNode.textContent = this.getHeadingsGroupLabel(option); + + if (option === 'all') { + option = 'important'; + } else { + option = 'all'; + } + + var menuitemNode = this.menuNode.querySelector( + '[data-id=skip-to-more-headings]' + ); + menuitemNode.setAttribute('data-show-heading-option', option); + + labelNode = menuitemNode.querySelector('span.label'); + labelNode.textContent = this.getShowMoreHeadingsLabel(option); + }, + + getLandmarksGroupLabel: function (option) { + if (option === 'all') { + return this.config.landmarkAllGroupLabel; + } + return this.config.landmarkImportantGroupLabel; + }, + + getShowMoreLandmarksSelector: function (option) { + if (option === 'all') { + return this.showAllLandmarksSelector; + } + return this.config.landmarks; + }, + + getShowMoreLandmarksLabel: function (option) { + var label, n; + + if (option === 'all') { + label = this.config.actionShowAllLandmarksLabel; + } else { + label = this.config.actionShowImportantLandmarksLabel; + } + + n = this.getLandmarks(this.getShowMoreLandmarksSelector(option)); + if (n && n.length) { + n = n.length; + } else { + n = '0'; + } + + return label.replace('$num', n); + }, + + addActionMoreLandmarks: function (groupNode) { + var item = {}; + item.name = this.getShowMoreLandmarksLabel('all'); + item.tagName = 'action'; + item.role = 'menuitem'; + item.class = 'action'; + item.dataId = 'skip-to-more-landmarks'; + var menuitemNode = this.addMenuitemToGroup(groupNode, item); + menuitemNode.setAttribute('data-show-landmark-option', 'all'); + menuitemNode.title = this.config.actionShowLandmarksHelp; + }, + + updateLandmarksGroupMenuitems: function (option) { + var selector = this.getShowMoreLandmarksSelector(option); + var landmarks = this.getLandmarks(selector); + var groupNode = document.getElementById('id-skip-to-group-landmarks'); + this.addMenuitemsToGroup( + groupNode, + landmarks, + this.config.msgNoLandmarksFound + ); + this.updateMenuitems(); + + // Move focus to first landmark menuitem + if (groupNode.firstElementChild) { + groupNode.firstElementChild.focus(); + } + + var labelNode = this.menuNode.querySelector( + '#id-skip-to-group-landmarks-label' + ); + labelNode.textContent = this.getLandmarksGroupLabel(option); + + if (option === 'all') { + option = 'important'; + } else { + option = 'all'; + } + + var menuitemNode = this.menuNode.querySelector( + '[data-id=skip-to-more-landmarks]' + ); + menuitemNode.setAttribute('data-show-landmark-option', option); + + labelNode = menuitemNode.querySelector('span.label'); + labelNode.textContent = this.getShowMoreLandmarksLabel(option); + }, + + createMenu: function () { + var groupNode, landmarkElems, headingElems; + // remove current menu items from menu + while (this.menuNode.lastElementChild) { + this.menuNode.removeChild(this.menuNode.lastElementChild); + } + + // Create landmarks group + landmarkElems = this.getLandmarks(); + groupNode = this.addMenuitemGroup( + 'id-skip-to-group-landmarks', + this.config.landmarkImportantGroupLabel + ); + this.addMenuitemsToGroup( + groupNode, + landmarkElems, + this.config.msgNoLandmarksFound + ); + + // Create headings group + headingElems = this.getHeadings(); + groupNode = this.addMenuitemGroup( + 'id-skip-to-group-headings', + this.config.headingImportantGroupLabel + ); + this.addMenuitemsToGroup( + groupNode, + headingElems, + this.config.msgNoHeadingsFound + ); + + // Create actions, if enabled + if (this.config.enableActions) { + groupNode = this.addMenuitemGroup( + 'id-skip-to-group-actions', + this.config.actionGroupLabel + ); + this.addActionMoreHeadings(groupNode); + this.addActionMoreLandmarks(groupNode); + } + + // Update list of menuitems + this.updateMenuitems(); + }, + + // + // Menu scripting event functions and utilities + // + + setFocusToMenuitem: function (menuitem) { + if (menuitem) { + menuitem.focus(); + } + }, + + setFocusToFirstMenuitem: function () { + this.setFocusToMenuitem(this.firstMenuitem); + }, + + setFocusToLastMenuitem: function () { + this.setFocusToMenuitem(this.lastMenuitem); + }, + + setFocusToPreviousMenuitem: function (menuitem) { + var newMenuitem, index; + if (menuitem === this.firstMenuitem) { + newMenuitem = this.lastMenuitem; + } else { + index = this.menuitemNodes.indexOf(menuitem); + newMenuitem = this.menuitemNodes[index - 1]; + } + this.setFocusToMenuitem(newMenuitem); + return newMenuitem; + }, + + setFocusToNextMenuitem: function (menuitem) { + var newMenuitem, index; + if (menuitem === this.lastMenuitem) { + newMenuitem = this.firstMenuitem; + } else { + index = this.menuitemNodes.indexOf(menuitem); + newMenuitem = this.menuitemNodes[index + 1]; + } + this.setFocusToMenuitem(newMenuitem); + return newMenuitem; + }, + + setFocusByFirstCharacter: function (menuitem, char) { + var start, index; + if (char.length > 1) { + return; + } + char = char.toLowerCase(); + + // Get start index for search based on position of currentItem + start = this.menuitemNodes.indexOf(menuitem) + 1; + if (start >= this.menuitemNodes.length) { + start = 0; + } + + // Check remaining items in the menu + index = this.firstChars.indexOf(char, start); + + // If not found in remaining items, check headings + if (index === -1) { + index = this.headingLevels.indexOf(char, start); + } + + // If not found in remaining items, check from beginning + if (index === -1) { + index = this.firstChars.indexOf(char, 0); + } + + // If not found in remaining items, check headings from beginning + if (index === -1) { + index = this.headingLevels.indexOf(char, 0); + } + + // If match was found... + if (index > -1) { + this.setFocusToMenuitem(this.menuitemNodes[index]); + } + }, + + // Utilities + getIndexFirstChars: function (startIndex, char) { + for (var i = startIndex; i < this.firstChars.length; i += 1) { + if (char === this.firstChars[i]) { + return i; + } + } + return -1; + }, + // Popup menu methods + openPopup: function () { + this.createMenu(); + this.menuNode.style.display = 'block'; + this.buttonNode.setAttribute('aria-expanded', 'true'); + }, + + closePopup: function () { + if (this.isOpen()) { + this.buttonNode.setAttribute('aria-expanded', 'false'); + this.menuNode.style.display = 'none'; + } + }, + isOpen: function () { + return this.buttonNode.getAttribute('aria-expanded') === 'true'; + }, + // Menu event handlers + handleFocusin: function () { + this.domNode.classList.add('focus'); + }, + handleFocusout: function () { + this.domNode.classList.remove('focus'); + }, + handleButtonKeydown: function (event) { + var key = event.key, + flag = false; + switch (key) { + case ' ': + case 'Enter': + case 'ArrowDown': + case 'Down': + this.openPopup(); + this.setFocusToFirstMenuitem(); + flag = true; + break; + case 'Esc': + case 'Escape': + this.closePopup(); + this.buttonNode.focus(); + flag = true; + break; + case 'Up': + case 'ArrowUp': + this.openPopup(); + this.setFocusToLastMenuitem(); + flag = true; + break; + default: + break; + } + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + }, + handleButtonClick: function (event) { + if (this.isOpen()) { + this.closePopup(); + this.buttonNode.focus(); + } else { + this.openPopup(); + this.setFocusToFirstMenuitem(); + } + event.stopPropagation(); + event.preventDefault(); + }, + skipToElement: function (menuitem) { + var inputNode = false; + var isSearch = menuitem.classList.contains('skip-to-search'); + var node = document.querySelector( + '[data-skip-to-id="' + menuitem.getAttribute('data-id') + '"]' + ); + if (node) { + if (isSearch) { + inputNode = node.querySelector('input'); + } + if (inputNode && this.isVisible(inputNode)) { + inputNode.focus(); + } else { + node.tabIndex = -1; + node.focus(); + } + } + }, + handleMenuitemAction: function (tgt) { + var option; + switch (tgt.getAttribute('data-id')) { + case '': + // this means there were no headings or landmarks in the list + break; + + case 'skip-to-more-headings': + option = tgt.getAttribute('data-show-heading-option'); + this.updateHeadingGroupMenuitems(option); + break; + + case 'skip-to-more-landmarks': + option = tgt.getAttribute('data-show-landmark-option'); + this.updateLandmarksGroupMenuitems(option); + break; + + default: + this.closePopup(); + this.skipToElement(tgt); + break; + } + }, + handleMenuitemKeydown: function (event) { + var tgt = event.currentTarget, + key = event.key, + flag = false; + + function isPrintableCharacter(str) { + return str.length === 1 && str.match(/\S/); + } + if (event.ctrlKey || event.altKey || event.metaKey) { + return; + } + if (event.shiftKey) { + if (isPrintableCharacter(key)) { + this.setFocusByFirstCharacter(tgt, key); + flag = true; + } + if (event.key === 'Tab') { + this.buttonNode.focus(); + this.closePopup(); + flag = true; + } + } else { + switch (key) { + case 'Enter': + case ' ': + this.handleMenuitemAction(tgt); + flag = true; + break; + case 'Esc': + case 'Escape': + this.closePopup(); + this.buttonNode.focus(); + flag = true; + break; + case 'Up': + case 'ArrowUp': + this.setFocusToPreviousMenuitem(tgt); + flag = true; + break; + case 'ArrowDown': + case 'Down': + this.setFocusToNextMenuitem(tgt); + flag = true; + break; + case 'Home': + case 'PageUp': + this.setFocusToFirstMenuitem(); + flag = true; + break; + case 'End': + case 'PageDown': + this.setFocusToLastMenuitem(); + flag = true; + break; + case 'Tab': + this.closePopup(); + break; + default: + if (isPrintableCharacter(key)) { + this.setFocusByFirstCharacter(tgt, key); + flag = true; + } + break; + } + } + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + }, + handleMenuitemClick: function (event) { + this.handleMenuitemAction(event.currentTarget); + event.stopPropagation(); + event.preventDefault(); + }, + handleMenuitemMouseover: function (event) { + var tgt = event.currentTarget; + tgt.focus(); + }, + handleBackgroundMousedown: function (event) { + if (!this.domNode.contains(event.target)) { + if (this.isOpen()) { + this.closePopup(); + this.buttonNode.focus(); + } + } + }, + // methods to extract lanndmarks, headings and ids + normalizeName: function (name) { + if (typeof name === 'string') + return name.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + return ''; + }, + getTextContent: function (elem) { + function getText(e, strings) { + // If text node get the text and return + if (e.nodeType === Node.TEXT_NODE) { + strings.push(e.data); + } else { + // if an element for through all the children elements looking for text + if (e.nodeType === Node.ELEMENT_NODE) { + // check to see if IMG or AREA element and to use ALT content if defined + var tagName = e.tagName.toLowerCase(); + if (tagName === 'img' || tagName === 'area') { + if (e.alt) { + strings.push(e.alt); + } + } else { + var c = e.firstChild; + while (c) { + getText(c, strings); + c = c.nextSibling; + } // end loop + } + } + } + } // end function getStrings + // Create return object + var str = 'Test', + strings = []; + getText(elem, strings); + if (strings.length) str = strings.join(' '); + return str; + }, + getAccessibleName: function (elem) { + var labelledbyIds = elem.getAttribute('aria-labelledby'), + label = elem.getAttribute('aria-label'), + title = elem.getAttribute('title'), + name = ''; + if (labelledbyIds && labelledbyIds.length) { + var str, + strings = [], + ids = labelledbyIds.split(' '); + if (!ids.length) ids = [labelledbyIds]; + for (var i = 0, l = ids.length; i < l; i += 1) { + var e = document.getElementById(ids[i]); + if (e) str = this.getTextContent(e); + if (str.length) strings.push(str); + } + name = strings.join(' '); + } else { + if (label && label.length) { + name = label; + } else { + if (title && title.length) { + name = title; + } + } + } + return name; + }, + isVisible: function (element) { + function isVisibleRec(el) { + if (el.nodeType === 9) + return true; /*IE8 does not support Node.DOCUMENT_NODE*/ + var computedStyle = window.getComputedStyle(el); + var display = computedStyle.getPropertyValue('display'); + var visibility = computedStyle.getPropertyValue('visibility'); + var hidden = el.getAttribute('hidden'); + if (display === 'none' || visibility === 'hidden' || hidden !== null) { + return false; + } + return isVisibleRec(el.parentNode); + } + return isVisibleRec(element); + }, + getHeadings: function (targets) { + var dataId; + if (typeof targets !== 'string') { + targets = this.config.headings; + } + var headingElementsArr = []; + if (typeof targets !== 'string' || targets.length === 0) return; + var headings = document.querySelectorAll(targets); + for (var i = 0, j = 0, len = headings.length; i < len; i += 1) { + var heading = headings[i]; + var role = heading.getAttribute('role'); + if (typeof role === 'string' && role === 'presentation') continue; + if (this.isVisible(heading)) { + if (heading.hasAttribute('data-skip-to-id')) { + dataId = heading.getAttribute('data-skip-to-id'); + } else { + heading.setAttribute('data-skip-to-id', this.skipToIdIndex); + dataId = this.skipToIdIndex; + } + var headingItem = {}; + headingItem.dataId = dataId.toString(); + headingItem.class = 'heading'; + headingItem.name = this.getTextContent(heading); + headingItem.tagName = heading.tagName.toLowerCase(); + headingItem.role = 'heading'; + headingElementsArr.push(headingItem); + j += 1; + this.skipToIdIndex += 1; + } + } + return headingElementsArr; + }, + getLocalizedLandmarkName: function (tagName, name) { + var n; + switch (tagName) { + case 'aside': + n = this.config.asideLabel; + break; + case 'footer': + n = this.config.footerLabel; + break; + case 'form': + n = this.config.formLabel; + break; + case 'header': + n = this.config.headerLabel; + break; + case 'main': + n = this.config.mainLabel; + break; + case 'nav': + n = this.config.navLabel; + break; + case 'search': + n = this.config.searchLabel; + break; + // When an ID is used as a selector, assume for main content + default: + n = this.config.mainLabel; + break; + } + if (this.isNotEmptyString(name)) { + n += ': ' + name; + } + return n; + }, + getLandmarks: function (targets) { + if (typeof targets !== 'string') { + targets = this.config.landmarks; + } + var landmarks = document.querySelectorAll(targets); + var mainElems = []; + var searchElems = []; + var navElems = []; + var asideElems = []; + var footerElems = []; + var otherElems = []; + var dataId = ''; + for (var i = 0, j = 0, len = landmarks.length; i < len; i = i + 1) { + var landmark = landmarks[i]; + // if skipto is a landmark don't include it in the list + if (landmark === this.domNode) { + continue; + } + var role = landmark.getAttribute('role'); + var tagName = landmark.tagName.toLowerCase(); + if (typeof role === 'string' && role === 'presentation') continue; + if (this.isVisible(landmark)) { + if (!role) role = landmark.tagName.toLowerCase(); + var name = this.getAccessibleName(landmark); + if (typeof name !== 'string') { + name = ''; + } + // normalize tagNames + switch (role) { + case 'banner': + tagName = 'header'; + break; + case 'complementary': + tagName = 'aside'; + break; + case 'contentinfo': + tagName = 'footer'; + break; + case 'form': + tagName = 'form'; + break; + case 'main': + tagName = 'main'; + break; + case 'navigation': + tagName = 'nav'; + break; + case 'search': + tagName = 'search'; + break; + default: + break; + } + // if using ID for selectQuery give tagName as main + if ( + [ + 'aside', + 'footer', + 'form', + 'header', + 'main', + 'nav', + 'search', + ].indexOf(tagName) < 0 + ) { + tagName = 'main'; + } + if (landmark.hasAttribute('data-skip-to-id')) { + dataId = landmark.getAttribute('data-skip-to-id'); + } else { + landmark.setAttribute('data-skip-to-id', this.skipToIdIndex); + dataId = this.skipToIdIndex; + } + var landmarkItem = {}; + landmarkItem.dataId = dataId.toString(); + landmarkItem.class = 'landmark'; + landmarkItem.name = this.getLocalizedLandmarkName(tagName, name); + landmarkItem.tagName = tagName; + j += 1; + this.skipToIdIndex += 1; + // For sorting landmarks into groups + switch (tagName) { + case 'main': + mainElems.push(landmarkItem); + break; + case 'search': + searchElems.push(landmarkItem); + break; + case 'nav': + navElems.push(landmarkItem); + break; + case 'aside': + asideElems.push(landmarkItem); + break; + case 'footer': + footerElems.push(landmarkItem); + break; + default: + otherElems.push(landmarkItem); + break; + } + } + } + return [].concat( + mainElems, + searchElems, + navElems, + asideElems, + footerElems, + otherElems + ); + }, + }; + // Initialize skipto menu button with onload event + window.addEventListener('load', function () { + SkipTo.init(window.SkipToConfig || window.Wordpress || {}); + console.log('SkipTo loaded...'); + }); +})(); +/*@end @*/ From 4fba50ebea9b3c2f51d03b17b5c48bf2d728caf3 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Mon, 23 Nov 2020 11:21:30 -0600 Subject: [PATCH 02/15] added skipto to some examples --- examples/carousel/carousel-1-prev-next.html | 1 + examples/carousel/carousel-2-tablist.html | 10 +--------- examples/js/skipto.js | 20 ++++++++++++++++++- ...menu-button-actions-active-descendant.html | 1 + examples/menu-button/menu-button-actions.html | 1 + examples/menu-button/menu-button-links.html | 1 + examples/radio/radio-activedescendant.html | 1 + examples/radio/radio.html | 1 + 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/examples/carousel/carousel-1-prev-next.html b/examples/carousel/carousel-1-prev-next.html index b36a0ec50b..b02fc6da9b 100644 --- a/examples/carousel/carousel-1-prev-next.html +++ b/examples/carousel/carousel-1-prev-next.html @@ -11,6 +11,7 @@ + diff --git a/examples/carousel/carousel-2-tablist.html b/examples/carousel/carousel-2-tablist.html index 08137bb7fa..715f5afa7f 100644 --- a/examples/carousel/carousel-2-tablist.html +++ b/examples/carousel/carousel-2-tablist.html @@ -12,15 +12,7 @@ - + diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 434196bb54..8c9d0924e4 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -4,6 +4,12 @@ /*@cc_on @*/ /*@if (@_jscript_version >= 5.8) @*/ /*jslint devel: true */ +/*! skipto - v3.1.0 - 2020-11-12 + * https://github.com/paypal/skipto + * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ +/*@cc_on @*/ +/*@if (@_jscript_version >= 5.8) @*/ +/*jslint devel: true */ /* ======================================================================== * Copyright (c) <2020> PayPal * All rights reserved. @@ -13,7 +19,9 @@ * Neither the name of PayPal or any of its subsidiaries or affiliates nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ======================================================================== */ + 'use strict'; + (function () { var SkipTo = { domNode: null, @@ -374,7 +382,7 @@ for (name in appConfigSettings) { //overwrite values of our local config, based on the external config if ( - typeof this.config[name] !== 'undefined' && + typeof localConfig[name] !== 'undefined' && ((typeof appConfigSettings[name] === 'string' && appConfigSettings[name].length > 0) || typeof appConfigSettings[name] === 'boolean') @@ -910,6 +918,7 @@ } else { node.tabIndex = -1; node.focus(); + node.scrollIntoView({ block: 'center' }); } } }, @@ -1290,3 +1299,12 @@ }); })(); /*@end @*/ + +var SkipToConfig = { + settings: { + skipTo: { + colorTheme: 'aria', + }, + }, +}; +/*@end @*/ diff --git a/examples/menu-button/menu-button-actions-active-descendant.html b/examples/menu-button/menu-button-actions-active-descendant.html index f23f216a56..6ea2105b93 100644 --- a/examples/menu-button/menu-button-actions-active-descendant.html +++ b/examples/menu-button/menu-button-actions-active-descendant.html @@ -9,6 +9,7 @@ + diff --git a/examples/menu-button/menu-button-actions.html b/examples/menu-button/menu-button-actions.html index 6868565bc0..acdf175037 100644 --- a/examples/menu-button/menu-button-actions.html +++ b/examples/menu-button/menu-button-actions.html @@ -9,6 +9,7 @@ + diff --git a/examples/menu-button/menu-button-links.html b/examples/menu-button/menu-button-links.html index 2e73e4a203..370c86db2d 100644 --- a/examples/menu-button/menu-button-links.html +++ b/examples/menu-button/menu-button-links.html @@ -10,6 +10,7 @@ + diff --git a/examples/radio/radio-activedescendant.html b/examples/radio/radio-activedescendant.html index 03e53e0d7e..b9e97b20d6 100644 --- a/examples/radio/radio-activedescendant.html +++ b/examples/radio/radio-activedescendant.html @@ -10,6 +10,7 @@ + diff --git a/examples/radio/radio.html b/examples/radio/radio.html index a1f08ca050..e062340803 100644 --- a/examples/radio/radio.html +++ b/examples/radio/radio.html @@ -10,6 +10,7 @@ + From dd58dcd58c42e6e432c879d3775b67eeb54f7735 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Mon, 23 Nov 2020 11:50:10 -0600 Subject: [PATCH 03/15] fixed linting bugs --- examples/js/skipto.js | 57 +++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 8c9d0924e4..4c407312e4 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -1,10 +1,4 @@ -/*! skipto - v3.1.0 - 2020-11-09 - * https://github.com/paypal/skipto - * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ -/*@cc_on @*/ -/*@if (@_jscript_version >= 5.8) @*/ -/*jslint devel: true */ -/*! skipto - v3.1.0 - 2020-11-12 +/*! skipto - v3.1.0 - 2020-11-23 * https://github.com/paypal/skipto * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ /*@cc_on @*/ @@ -705,33 +699,33 @@ }, createMenu: function () { - var groupNode, landmarkElems, headingElems; + var groupNode, landmarkElements, headingElements; // remove current menu items from menu while (this.menuNode.lastElementChild) { this.menuNode.removeChild(this.menuNode.lastElementChild); } // Create landmarks group - landmarkElems = this.getLandmarks(); + landmarkElements = this.getLandmarks(); groupNode = this.addMenuitemGroup( 'id-skip-to-group-landmarks', this.config.landmarkImportantGroupLabel ); this.addMenuitemsToGroup( groupNode, - landmarkElems, + landmarkElements, this.config.msgNoLandmarksFound ); // Create headings group - headingElems = this.getHeadings(); + headingElements = this.getHeadings(); groupNode = this.addMenuitemGroup( 'id-skip-to-group-headings', this.config.headingImportantGroupLabel ); this.addMenuitemsToGroup( groupNode, - headingElems, + headingElements, this.config.msgNoHeadingsFound ); @@ -1032,7 +1026,7 @@ } } }, - // methods to extract lanndmarks, headings and ids + // methods to extract landmarks, headings and ids normalizeName: function (name) { if (typeof name === 'string') return name.replace(/\w\S*/g, function (txt) { @@ -1184,12 +1178,12 @@ targets = this.config.landmarks; } var landmarks = document.querySelectorAll(targets); - var mainElems = []; - var searchElems = []; - var navElems = []; - var asideElems = []; - var footerElems = []; - var otherElems = []; + var mainElements = []; + var searchElements = []; + var navElements = []; + var asideElements = []; + var footerElements = []; + var otherElements = []; var dataId = ''; for (var i = 0, j = 0, len = landmarks.length; i < len; i = i + 1) { var landmark = landmarks[i]; @@ -1262,33 +1256,33 @@ // For sorting landmarks into groups switch (tagName) { case 'main': - mainElems.push(landmarkItem); + mainElements.push(landmarkItem); break; case 'search': - searchElems.push(landmarkItem); + searchElements.push(landmarkItem); break; case 'nav': - navElems.push(landmarkItem); + navElements.push(landmarkItem); break; case 'aside': - asideElems.push(landmarkItem); + asideElements.push(landmarkItem); break; case 'footer': - footerElems.push(landmarkItem); + footerElements.push(landmarkItem); break; default: - otherElems.push(landmarkItem); + otherElements.push(landmarkItem); break; } } } return [].concat( - mainElems, - searchElems, - navElems, - asideElems, - footerElems, - otherElems + mainElements, + searchElements, + navElements, + asideElements, + footerElements, + otherElements ); }, }; @@ -1298,7 +1292,6 @@ console.log('SkipTo loaded...'); }); })(); -/*@end @*/ var SkipToConfig = { settings: { From ff93e3dbab069627dc599f45bda5b8550ed89709 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Mon, 23 Nov 2020 11:56:11 -0600 Subject: [PATCH 04/15] fixed spelling problems found in javascript --- examples/js/skipto.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 4c407312e4..7f4c1290a6 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -1,11 +1,6 @@ /*! skipto - v3.1.0 - 2020-11-23 * https://github.com/paypal/skipto - * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ -/*@cc_on @*/ -/*@if (@_jscript_version >= 5.8) @*/ -/*jslint devel: true */ -/* ======================================================================== - * Copyright (c) <2020> PayPal + * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD * All rights reserved. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -151,7 +146,7 @@ }, }, defaultCSS: - '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].landmark .label,.skip-to [role=menuitem].noitems .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', + '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].landmark .label,.skip-to [role=menuitem].no-items .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', // // Functions related to configuring the features @@ -526,8 +521,8 @@ if (menuitems.length === 0) { var item = {}; item.name = msgNoItemsFound; - item.tagName = 'notag'; - item.class = 'noitems'; + item.tagName = 'no tag'; + item.class = 'no-items'; item.dataId = ''; this.addMenuitemToGroup(groupNode, item); } else { From 84d60b8a65d810d9c32e6d485356188490521ee7 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 1 Dec 2020 12:58:32 -0600 Subject: [PATCH 05/15] added indenting of child landmarks --- examples/js/skipto.js | 83 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 8 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 7f4c1290a6..dc9a12b29b 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -1,6 +1,11 @@ -/*! skipto - v3.1.0 - 2020-11-23 +/*! skipto - v3.1.0 - 2020-12-01 * https://github.com/paypal/skipto - * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD + * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ +/*@cc_on @*/ +/*@if (@_jscript_version >= 5.8) @*/ +/*jslint devel: true */ +/* ======================================================================== + * Copyright (c) <2020> PayPal * All rights reserved. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -54,7 +59,8 @@ headingAllGroupLabel: 'All Headings', mainLabel: 'main', searchLabel: 'search', - navLabel: 'menu', + navLabel: 'nav', + regionLabel: 'region', asideLabel: 'aside', footerLabel: 'footer', headerLabel: 'header', @@ -146,7 +152,7 @@ }, }, defaultCSS: - '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].landmark .label,.skip-to [role=menuitem].no-items .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', + '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .nesting{grid-column:1}.skip-to [role=menuitem].skip-to-nesting-level-2 .nesting{grid-column:2}.skip-to [role=menuitem].skip-to-nesting-level-3 .nesting{grid-column:3}.skip-to [role=menuitem].skip-to-nesting-level-0 .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-nesting-level-2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-nesting-level-3 .label{grid-column:4/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].no-items .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', // // Functions related to configuring the features @@ -443,7 +449,7 @@ }, addMenuitemToGroup: function (groupNode, mi) { - var tagNode, tagNodeChild, labelNode; + var tagNode, tagNodeChild, labelNode, nestingNode; var menuitemNode = document.createElement('div'); menuitemNode.setAttribute('role', 'menuitem'); @@ -488,6 +494,20 @@ } } + // add nesting level for landmarks + if (mi.class.includes('landmark')) { + menuitemNode.setAttribute('data-nesting', mi.nestingLevel); + menuitemNode.classList.add('skip-to-nesting-level-' + mi.nestingLevel); + + if (mi.nestingLevel > 0 && mi.nestingLevel > this.lastNestingLevel) { + nestingNode = document.createElement('span'); + // nestingNode.appendChild(document.createTextNode('\u2514')); + nestingNode.classList.add('nesting'); + menuitemNode.append(nestingNode); + } + this.lastNestingLevel = mi.nestingLevel; + } + labelNode = document.createElement('span'); labelNode.appendChild(document.createTextNode(mi.name)); labelNode.classList.add('label'); @@ -517,6 +537,7 @@ addMenuitemsToGroup: function (groupNode, menuitems, msgNoItemsFound) { groupNode.innerHTML = ''; + this.lastNestingLevel = 0; if (menuitems.length === 0) { var item = {}; @@ -659,7 +680,7 @@ updateLandmarksGroupMenuitems: function (option) { var selector = this.getShowMoreLandmarksSelector(option); - var landmarks = this.getLandmarks(selector); + var landmarks = this.getLandmarks(selector, option === 'all'); var groupNode = document.getElementById('id-skip-to-group-landmarks'); this.addMenuitemsToGroup( groupNode, @@ -1155,6 +1176,9 @@ case 'nav': n = this.config.navLabel; break; + case 'region': + n = this.config.regionLabel; + break; case 'search': n = this.config.searchLabel; break; @@ -1168,7 +1192,28 @@ } return n; }, - getLandmarks: function (targets) { + getNestingLevel: function (landmark, landmarks) { + var nestingLevel = 0; + var parentNode = landmark.parentNode; + while (parentNode) { + for (var i = 0; i < landmarks.length; i += 1) { + if (landmarks[i] === parentNode) { + nestingLevel += 1; + // no more than 3 levels of nesting supported + if (nestingLevel === 3) { + return 3; + } + continue; + } + } + parentNode = parentNode.parentNode; + } + return nestingLevel; + }, + getLandmarks: function (targets, allFlag) { + if (typeof allFlag !== 'boolean') { + allFlag = false; + } if (typeof targets !== 'string') { targets = this.config.landmarks; } @@ -1178,8 +1223,11 @@ var navElements = []; var asideElements = []; var footerElements = []; + var regionElements = []; var otherElements = []; + var allLandmarks = []; var dataId = ''; + console.log('\n\n== [getLandmarks] =='); for (var i = 0, j = 0, len = landmarks.length; i < len; i = i + 1) { var landmark = landmarks[i]; // if skipto is a landmark don't include it in the list @@ -1190,7 +1238,7 @@ var tagName = landmark.tagName.toLowerCase(); if (typeof role === 'string' && role === 'presentation') continue; if (this.isVisible(landmark)) { - if (!role) role = landmark.tagName.toLowerCase(); + if (!role) role = tagName; var name = this.getAccessibleName(landmark); if (typeof name !== 'string') { name = ''; @@ -1215,6 +1263,9 @@ case 'navigation': tagName = 'nav'; break; + case 'section': + tagName = 'region'; + break; case 'search': tagName = 'search'; break; @@ -1230,6 +1281,7 @@ 'header', 'main', 'nav', + 'region', 'search', ].indexOf(tagName) < 0 ) { @@ -1246,8 +1298,16 @@ landmarkItem.class = 'landmark'; landmarkItem.name = this.getLocalizedLandmarkName(tagName, name); landmarkItem.tagName = tagName; + landmarkItem.nestingLevel = 0; + if (allFlag) { + landmarkItem.nestingLevel = this.getNestingLevel( + landmark, + landmarks + ); + } j += 1; this.skipToIdIndex += 1; + allLandmarks.push(landmarkItem); // For sorting landmarks into groups switch (tagName) { case 'main': @@ -1265,14 +1325,21 @@ case 'footer': footerElements.push(landmarkItem); break; + case 'region': + regionElements.push(landmarkItem); + break; default: otherElements.push(landmarkItem); break; } } } + if (allFlag) { + return allLandmarks; + } return [].concat( mainElements, + regionElements, searchElements, navElements, asideElements, From c25a36178229f1c7b21c30d3df8b1596b8f1e824 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 1 Dec 2020 16:15:06 -0600 Subject: [PATCH 06/15] fixed linting issues and skip to position --- examples/js/skipto.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index dc9a12b29b..fdcec7b496 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -135,7 +135,7 @@ menuitemFocusBorderColor: '#ff552e', }, aria: { - positionLeft: '380px', + positionLeft: '', buttonColor: '#005a9c;', buttonBackgroundColor: '#def', buttonBorderColor: '#def', @@ -1131,7 +1131,7 @@ var headingElementsArr = []; if (typeof targets !== 'string' || targets.length === 0) return; var headings = document.querySelectorAll(targets); - for (var i = 0, j = 0, len = headings.length; i < len; i += 1) { + for (var i = 0, len = headings.length; i < len; i += 1) { var heading = headings[i]; var role = heading.getAttribute('role'); if (typeof role === 'string' && role === 'presentation') continue; @@ -1149,7 +1149,6 @@ headingItem.tagName = heading.tagName.toLowerCase(); headingItem.role = 'heading'; headingElementsArr.push(headingItem); - j += 1; this.skipToIdIndex += 1; } } @@ -1228,7 +1227,7 @@ var allLandmarks = []; var dataId = ''; console.log('\n\n== [getLandmarks] =='); - for (var i = 0, j = 0, len = landmarks.length; i < len; i = i + 1) { + for (var i = 0, len = landmarks.length; i < len; i = i + 1) { var landmark = landmarks[i]; // if skipto is a landmark don't include it in the list if (landmark === this.domNode) { @@ -1305,7 +1304,6 @@ landmarks ); } - j += 1; this.skipToIdIndex += 1; allLandmarks.push(landmarkItem); // For sorting landmarks into groups @@ -1349,17 +1347,18 @@ }, }; // Initialize skipto menu button with onload event + var SkipToConfig = { + settings: { + skipTo: { + colorTheme: 'aria', + }, + }, + }; + window.addEventListener('load', function () { - SkipTo.init(window.SkipToConfig || window.Wordpress || {}); + SkipTo.init(SkipToConfig); console.log('SkipTo loaded...'); }); })(); -var SkipToConfig = { - settings: { - skipTo: { - colorTheme: 'aria', - }, - }, -}; /*@end @*/ From 21fdc1fb218445dbfb826d15db112be5bfcd5a1c Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 1 Dec 2020 18:46:48 -0600 Subject: [PATCH 07/15] fixed spelling issue --- examples/js/skipto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index fdcec7b496..5707083e24 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -3,7 +3,7 @@ * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ /*@cc_on @*/ /*@if (@_jscript_version >= 5.8) @*/ -/*jslint devel: true */ +/*jslint developer: true */ /* ======================================================================== * Copyright (c) <2020> PayPal * All rights reserved. From 83d15677ad8bc734d089369bef2f393368b7c0f7 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 1 Dec 2020 19:10:01 -0600 Subject: [PATCH 08/15] fixed linting errors --- examples/js/skipto.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 5707083e24..7f98271379 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -2,7 +2,7 @@ * https://github.com/paypal/skipto * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ /*@cc_on @*/ -/*@if (@_jscript_version >= 5.8) @*/ +/*@if (@_javascript_version >= 5.8) @*/ /*jslint developer: true */ /* ======================================================================== * Copyright (c) <2020> PayPal @@ -135,7 +135,7 @@ menuitemFocusBorderColor: '#ff552e', }, aria: { - positionLeft: '', + positionLeft: '380px', buttonColor: '#005a9c;', buttonBackgroundColor: '#def', buttonBorderColor: '#def', @@ -1226,8 +1226,7 @@ var otherElements = []; var allLandmarks = []; var dataId = ''; - console.log('\n\n== [getLandmarks] =='); - for (var i = 0, len = landmarks.length; i < len; i = i + 1) { + for (var i = 0, len = landmarks.length; i < len; i += 1) { var landmark = landmarks[i]; // if skipto is a landmark don't include it in the list if (landmark === this.domNode) { @@ -1360,5 +1359,4 @@ console.log('SkipTo loaded...'); }); })(); - /*@end @*/ From 6ee05154de9b1e6d46699d7a8f8af8023e0b5453 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 2 Dec 2020 15:59:06 -0600 Subject: [PATCH 09/15] added support for aria-roledescription and using aria-label for heading menu items to make labels more like screen reader --- examples/js/skipto.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 7f98271379..1654108dce 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -57,6 +57,7 @@ landmarkAllGroupLabel: 'All Landmarks', headingImportantGroupLabel: 'Important Headings', headingAllGroupLabel: 'All Headings', + headingLevelLabel: 'Heading level', mainLabel: 'main', searchLabel: 'search', navLabel: 'nav', @@ -135,7 +136,7 @@ menuitemFocusBorderColor: '#ff552e', }, aria: { - positionLeft: '380px', + positionLeft: '', buttonColor: '#005a9c;', buttonBackgroundColor: '#def', buttonBorderColor: '#def', @@ -449,7 +450,7 @@ }, addMenuitemToGroup: function (groupNode, mi) { - var tagNode, tagNodeChild, labelNode, nestingNode; + var tagNode, tagNodeChild, labelNode, nestingNode, accName; var menuitemNode = document.createElement('div'); menuitemNode.setAttribute('role', 'menuitem'); @@ -478,9 +479,7 @@ if (this.config.enableHeadingLevelShortcuts) { tagNode = document.createElement('span'); tagNodeChild = document.createElement('span'); - tagNodeChild.appendChild( - document.createTextNode(mi.tagName.substring(1)) - ); + tagNodeChild.appendChild(document.createTextNode(mi.level)); tagNode.append(tagNodeChild); tagNode.appendChild(document.createTextNode(')')); tagNode.classList.add('level'); @@ -488,10 +487,13 @@ } else { menuitemNode.classList.add('no-level'); } - menuitemNode.setAttribute('data-level', mi.tagName.substring(1)); + menuitemNode.setAttribute('data-level', mi.level); if (mi.tagName && mi.tagName.length) { menuitemNode.classList.add('skip-to-' + mi.tagName); } + accName = mi.name + ', '; + accName += this.config.headingLevelLabel + ' ' + mi.level; + menuitemNode.setAttribute('aria-label', accName); } // add nesting level for landmarks @@ -1148,6 +1150,7 @@ headingItem.name = this.getTextContent(heading); headingItem.tagName = heading.tagName.toLowerCase(); headingItem.role = 'heading'; + headingItem.level = heading.tagName.substring(1); headingElementsArr.push(headingItem); this.skipToIdIndex += 1; } @@ -1183,7 +1186,7 @@ break; // When an ID is used as a selector, assume for main content default: - n = this.config.mainLabel; + n = tagName; break; } if (this.isNotEmptyString(name)) { @@ -1285,6 +1288,9 @@ ) { tagName = 'main'; } + if (landmark.hasAttribute('aria-roledescription')) { + tagName = landmark.getAttribute('aria-roledescription'); + } if (landmark.hasAttribute('data-skip-to-id')) { dataId = landmark.getAttribute('data-skip-to-id'); } else { @@ -1345,6 +1351,7 @@ ); }, }; + // Initialize skipto menu button with onload event var SkipToConfig = { settings: { From 182ff4626c031c4f2113bf1bab4c6be602aec3a4 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 3 Dec 2020 11:21:06 -0600 Subject: [PATCH 10/15] use NAVIGATION rather than NAV for navigation landmarks in menu --- examples/js/skipto.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index 1654108dce..fb148d4c7d 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -1,4 +1,4 @@ -/*! skipto - v3.1.0 - 2020-12-01 +/*! skipto - v3.1.0 - 2020-12-03 * https://github.com/paypal/skipto * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ /*@cc_on @*/ @@ -60,7 +60,7 @@ headingLevelLabel: 'Heading level', mainLabel: 'main', searchLabel: 'search', - navLabel: 'nav', + navLabel: 'navigation', regionLabel: 'region', asideLabel: 'aside', footerLabel: 'footer', From 173f5177ddb5a5dcc305dc6f22ae3e346b1f1509 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Thu, 3 Dec 2020 12:12:45 -0600 Subject: [PATCH 11/15] added aria-label to action menus --- examples/js/skipto.js | 68 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index fb148d4c7d..b8cc8a66dd 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -80,6 +80,11 @@ actionShowImportantLandmarksLabel: 'Show Important landmarks ($num)', actionShowAllLandmarksLabel: 'Show All landmarks ($num)', + actionShowImportantHeadingsAriaLabel: 'Show $num Important Headings', + actionShowAllHeadingsAriaLabel: 'Show All $num headings', + actionShowImportantLandmarksAriaLabel: 'Show $num Important landmarks', + actionShowAllLandmarksAriaLabel: 'Show All $num landmarks', + // Selectors for landmark and headings sections landmarks: 'main, [role="main"], [role="search"], nav, [role="navigation"], aside, [role="complementary"]', @@ -450,13 +455,16 @@ }, addMenuitemToGroup: function (groupNode, mi) { - var tagNode, tagNodeChild, labelNode, nestingNode, accName; + var tagNode, tagNodeChild, labelNode, nestingNode; var menuitemNode = document.createElement('div'); menuitemNode.setAttribute('role', 'menuitem'); menuitemNode.classList.add(mi.class); menuitemNode.setAttribute('data-id', mi.dataId); menuitemNode.tabIndex = -1; + if (mi.ariaLabel) { + menuitemNode.setAttribute('aria-label', mi.ariaLabel); + } // add event handlers menuitemNode.addEventListener( @@ -491,9 +499,6 @@ if (mi.tagName && mi.tagName.length) { menuitemNode.classList.add('skip-to-' + mi.tagName); } - accName = mi.name + ', '; - accName += this.config.headingLevelLabel + ' ' + mi.level; - menuitemNode.setAttribute('aria-label', accName); } // add nesting level for landmarks @@ -503,7 +508,6 @@ if (mi.nestingLevel > 0 && mi.nestingLevel > this.lastNestingLevel) { nestingNode = document.createElement('span'); - // nestingNode.appendChild(document.createTextNode('\u2514')); nestingNode.classList.add('nesting'); menuitemNode.append(nestingNode); } @@ -587,9 +591,28 @@ return label.replace('$num', n); }, + getShowMoreHeadingsAriaLabel: function (option) { + var label, n; + + label = this.config.actionShowImportantHeadingsAriaLabel; + + if (option === 'all') { + label = this.config.actionShowAllHeadingsAriaLabel; + } + n = this.getHeadings(this.getShowMoreHeadingsSelector(option)); + if (n && n.length) { + n = n.length; + } else { + n = '0'; + } + + return label.replace('$num', n); + }, + addActionMoreHeadings: function (groupNode) { var item = {}; item.name = this.getShowMoreHeadingsLabel('all'); + item.ariaLabel = this.getShowMoreHeadingsAriaLabel('all'); item.tagName = 'action'; item.role = 'menuitem'; item.class = 'action'; @@ -630,6 +653,10 @@ '[data-id=skip-to-more-headings]' ); menuitemNode.setAttribute('data-show-heading-option', option); + menuitemNode.setAttribute( + 'aria-label', + this.getShowMoreHeadingsAriaLabel(option) + ); labelNode = menuitemNode.querySelector('span.label'); labelNode.textContent = this.getShowMoreHeadingsLabel(option); @@ -668,9 +695,29 @@ return label.replace('$num', n); }, + getShowMoreLandmarksAriaLabel: function (option) { + var label, n; + + if (option === 'all') { + label = this.config.actionShowAllLandmarksAriaLabel; + } else { + label = this.config.actionShowImportantLandmarksAriaLabel; + } + + n = this.getLandmarks(this.getShowMoreLandmarksSelector(option)); + if (n && n.length) { + n = n.length; + } else { + n = '0'; + } + + return label.replace('$num', n); + }, + addActionMoreLandmarks: function (groupNode) { var item = {}; item.name = this.getShowMoreLandmarksLabel('all'); + item.ariaLabel = this.getShowMoreLandmarksAriaLabel('all'); item.tagName = 'action'; item.role = 'menuitem'; item.class = 'action'; @@ -711,6 +758,10 @@ '[data-id=skip-to-more-landmarks]' ); menuitemNode.setAttribute('data-show-landmark-option', option); + menuitemNode.setAttribute( + 'aria-label', + this.getShowMoreLandmarksAriaLabel(option) + ); labelNode = menuitemNode.querySelector('span.label'); labelNode.textContent = this.getShowMoreLandmarksLabel(option); @@ -1126,7 +1177,7 @@ return isVisibleRec(element); }, getHeadings: function (targets) { - var dataId; + var dataId, level; if (typeof targets !== 'string') { targets = this.config.headings; } @@ -1144,13 +1195,16 @@ heading.setAttribute('data-skip-to-id', this.skipToIdIndex); dataId = this.skipToIdIndex; } + level = heading.tagName.substring(1); var headingItem = {}; headingItem.dataId = dataId.toString(); headingItem.class = 'heading'; headingItem.name = this.getTextContent(heading); + headingItem.ariaLabel = headingItem.name + ', '; + headingItem.ariaLabel += this.config.headingLevelLabel + ' ' + level; headingItem.tagName = heading.tagName.toLowerCase(); headingItem.role = 'heading'; - headingItem.level = heading.tagName.substring(1); + headingItem.level = level; headingElementsArr.push(headingItem); this.skipToIdIndex += 1; } From 613288de0f24ea7c735a16b386f1b543c12159e5 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Mon, 7 Dec 2020 10:09:54 -0600 Subject: [PATCH 12/15] updated coding practices to use function names in aria practices style guide --- examples/js/skipto.js | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/js/skipto.js b/examples/js/skipto.js index b8cc8a66dd..f662639d5e 100644 --- a/examples/js/skipto.js +++ b/examples/js/skipto.js @@ -300,7 +300,7 @@ } } this.addCSSColors(); - this.addStyleElement(this.defaultCSS); + this.renderStyleElement(this.defaultCSS); this.domNode = document.createElement(this.config.containerElement); this.domNode.classList.add('skip-to'); if (this.isNotEmptyString(this.config.customClass)) { @@ -396,7 +396,7 @@ } } }, - addStyleElement: function (cssString) { + renderStyleElement: function (cssString) { var styleNode = document.createElement('style'); var headNode = document.getElementsByTagName('head')[0]; var css = document.createTextNode(cssString); @@ -454,7 +454,7 @@ this.updateKeyboardShortCuts(); }, - addMenuitemToGroup: function (groupNode, mi) { + renderMenuitemToGroup: function (groupNode, mi) { var tagNode, tagNodeChild, labelNode, nestingNode; var menuitemNode = document.createElement('div'); @@ -522,7 +522,7 @@ return menuitemNode; }, - addMenuitemGroup: function (groupId, title) { + renderMenuitemGroup: function (groupId, title) { var labelNode, groupNode; var menuNode = this.menuNode; if (title) { @@ -541,7 +541,7 @@ return groupNode; }, - addMenuitemsToGroup: function (groupNode, menuitems, msgNoItemsFound) { + renderMenuitemsToGroup: function (groupNode, menuitems, msgNoItemsFound) { groupNode.innerHTML = ''; this.lastNestingLevel = 0; @@ -551,10 +551,10 @@ item.tagName = 'no tag'; item.class = 'no-items'; item.dataId = ''; - this.addMenuitemToGroup(groupNode, item); + this.renderMenuitemToGroup(groupNode, item); } else { for (var i = 0; i < menuitems.length; i += 1) { - this.addMenuitemToGroup(groupNode, menuitems[i]); + this.renderMenuitemToGroup(groupNode, menuitems[i]); } } }, @@ -609,7 +609,7 @@ return label.replace('$num', n); }, - addActionMoreHeadings: function (groupNode) { + renderActionMoreHeadings: function (groupNode) { var item = {}; item.name = this.getShowMoreHeadingsLabel('all'); item.ariaLabel = this.getShowMoreHeadingsAriaLabel('all'); @@ -617,7 +617,7 @@ item.role = 'menuitem'; item.class = 'action'; item.dataId = 'skip-to-more-headings'; - var menuitemNode = this.addMenuitemToGroup(groupNode, item); + var menuitemNode = this.renderMenuitemToGroup(groupNode, item); menuitemNode.setAttribute('data-show-heading-option', 'all'); menuitemNode.title = this.config.actionShowHeadingsHelp; }, @@ -626,7 +626,7 @@ var selector = this.getShowMoreHeadingsSelector(option); var headings = this.getHeadings(selector); var groupNode = document.getElementById('id-skip-to-group-headings'); - this.addMenuitemsToGroup( + this.renderMenuitemsToGroup( groupNode, headings, this.config.msgNoHeadingsFound @@ -714,7 +714,7 @@ return label.replace('$num', n); }, - addActionMoreLandmarks: function (groupNode) { + renderActionMoreLandmarks: function (groupNode) { var item = {}; item.name = this.getShowMoreLandmarksLabel('all'); item.ariaLabel = this.getShowMoreLandmarksAriaLabel('all'); @@ -722,7 +722,7 @@ item.role = 'menuitem'; item.class = 'action'; item.dataId = 'skip-to-more-landmarks'; - var menuitemNode = this.addMenuitemToGroup(groupNode, item); + var menuitemNode = this.renderMenuitemToGroup(groupNode, item); menuitemNode.setAttribute('data-show-landmark-option', 'all'); menuitemNode.title = this.config.actionShowLandmarksHelp; }, @@ -731,7 +731,7 @@ var selector = this.getShowMoreLandmarksSelector(option); var landmarks = this.getLandmarks(selector, option === 'all'); var groupNode = document.getElementById('id-skip-to-group-landmarks'); - this.addMenuitemsToGroup( + this.renderMenuitemsToGroup( groupNode, landmarks, this.config.msgNoLandmarksFound @@ -767,7 +767,7 @@ labelNode.textContent = this.getShowMoreLandmarksLabel(option); }, - createMenu: function () { + renderMenu: function () { var groupNode, landmarkElements, headingElements; // remove current menu items from menu while (this.menuNode.lastElementChild) { @@ -776,11 +776,11 @@ // Create landmarks group landmarkElements = this.getLandmarks(); - groupNode = this.addMenuitemGroup( + groupNode = this.renderMenuitemGroup( 'id-skip-to-group-landmarks', this.config.landmarkImportantGroupLabel ); - this.addMenuitemsToGroup( + this.renderMenuitemsToGroup( groupNode, landmarkElements, this.config.msgNoLandmarksFound @@ -788,11 +788,11 @@ // Create headings group headingElements = this.getHeadings(); - groupNode = this.addMenuitemGroup( + groupNode = this.renderMenuitemGroup( 'id-skip-to-group-headings', this.config.headingImportantGroupLabel ); - this.addMenuitemsToGroup( + this.renderMenuitemsToGroup( groupNode, headingElements, this.config.msgNoHeadingsFound @@ -800,12 +800,12 @@ // Create actions, if enabled if (this.config.enableActions) { - groupNode = this.addMenuitemGroup( + groupNode = this.renderMenuitemGroup( 'id-skip-to-group-actions', this.config.actionGroupLabel ); - this.addActionMoreHeadings(groupNode); - this.addActionMoreLandmarks(groupNode); + this.renderActionMoreHeadings(groupNode); + this.renderActionMoreLandmarks(groupNode); } // Update list of menuitems @@ -902,7 +902,7 @@ }, // Popup menu methods openPopup: function () { - this.createMenu(); + this.renderMenu(); this.menuNode.style.display = 'block'; this.buttonNode.setAttribute('aria-expanded', 'true'); }, From daae172dfb977a8accaaa43e51798f2c8169d0f6 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Fri, 18 Dec 2020 15:51:38 -0600 Subject: [PATCH 13/15] updated to use jumpto instead of skipto --- examples/carousel/carousel-1-prev-next.html | 2 +- examples/carousel/carousel-2-tablist.html | 2 +- examples/js/{skipto.js => jumpto.js} | 179 +++++++----------- ...menu-button-actions-active-descendant.html | 2 +- examples/menu-button/menu-button-actions.html | 2 +- examples/menu-button/menu-button-links.html | 2 +- examples/radio/radio-activedescendant.html | 2 +- examples/radio/radio.html | 2 +- 8 files changed, 72 insertions(+), 121 deletions(-) rename examples/js/{skipto.js => jumpto.js} (82%) diff --git a/examples/carousel/carousel-1-prev-next.html b/examples/carousel/carousel-1-prev-next.html index b02fc6da9b..75c091dcce 100644 --- a/examples/carousel/carousel-1-prev-next.html +++ b/examples/carousel/carousel-1-prev-next.html @@ -11,7 +11,7 @@ - + diff --git a/examples/carousel/carousel-2-tablist.html b/examples/carousel/carousel-2-tablist.html index 715f5afa7f..5d99eb4205 100644 --- a/examples/carousel/carousel-2-tablist.html +++ b/examples/carousel/carousel-2-tablist.html @@ -11,7 +11,7 @@ - + diff --git a/examples/js/skipto.js b/examples/js/jumpto.js similarity index 82% rename from examples/js/skipto.js rename to examples/js/jumpto.js index f662639d5e..565855f53a 100644 --- a/examples/js/skipto.js +++ b/examples/js/jumpto.js @@ -1,23 +1,17 @@ -/*! skipto - v3.1.0 - 2020-12-03 - * https://github.com/paypal/skipto - * Copyright (c) 2020 PayPal Accessibility Team and University of Illinois; Licensed BSD */ -/*@cc_on @*/ -/*@if (@_javascript_version >= 5.8) @*/ -/*jslint developer: true */ -/* ======================================================================== - * Copyright (c) <2020> PayPal - * All rights reserved. - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of PayPal or any of its subsidiaries or affiliates nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ======================================================================== */ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + * + * File: jumpto.js + * + * Desc: Jump to provides keyboard navigation to document structure + * This feature is based on the ARIA APG menu button example + */ 'use strict'; (function () { - var SkipTo = { + var JumpTo = { domNode: null, buttonNode: null, menuNode: null, @@ -49,14 +43,14 @@ accesskeyNotSupported: ' is not supported on this browser.', buttonTitle: 'Keyboard Navigation', buttonTitleWithAccesskey: 'Keyboard Navigation\nAccesskey is "$key"', - buttonLabel: 'Skip To Content', + buttonLabel: 'Jump To Content', // Menu labels and messages menuLabel: 'Landmarks and Headings', - landmarkImportantGroupLabel: 'Important Landmarks', - landmarkAllGroupLabel: 'All Landmarks', - headingImportantGroupLabel: 'Important Headings', - headingAllGroupLabel: 'All Headings', + landmarkSelectedGroupLabel: 'Landmarks', + landmarkAllGroupLabel: 'Landmarks', + headingSelectedGroupLabel: 'Headings', + headingAllGroupLabel: 'Headings', headingLevelLabel: 'Heading level', mainLabel: 'main', searchLabel: 'search', @@ -72,18 +66,18 @@ // Action labels and messages actionGroupLabel: 'Actions', actionShowHeadingsHelp: - 'Toggles between showing "All" and "Important" headings.', - actionShowImportantHeadingsLabel: 'Show Important Headings ($num)', - actionShowAllHeadingsLabel: 'Show All headings ($num)', + 'Toggles between showing "All" and "Selected" Headings.', + actionShowSelectedHeadingsLabel: 'Show Selected Headings ($num)', + actionShowAllHeadingsLabel: 'Show All Headings ($num)', actionShowLandmarksHelp: - 'Toggles between showing "All" and "Important" landmarks.', - actionShowImportantLandmarksLabel: 'Show Important landmarks ($num)', - actionShowAllLandmarksLabel: 'Show All landmarks ($num)', + 'Toggles between showing "All" and "Selected" Landmarks.', + actionShowSelectedLandmarksLabel: 'Show Selected Landmarks ($num)', + actionShowAllLandmarksLabel: 'Show All Landmarks ($num)', - actionShowImportantHeadingsAriaLabel: 'Show $num Important Headings', - actionShowAllHeadingsAriaLabel: 'Show All $num headings', - actionShowImportantLandmarksAriaLabel: 'Show $num Important landmarks', - actionShowAllLandmarksAriaLabel: 'Show All $num landmarks', + actionShowSelectedHeadingsAriaLabel: 'Show $num selected headings', + actionShowAllHeadingsAriaLabel: 'Show all $num headings', + actionShowSelectedLandmarksAriaLabel: 'Show $num selected landmarks', + actionShowAllLandmarksAriaLabel: 'Show all $num landmarks', // Selectors for landmark and headings sections landmarks: @@ -108,39 +102,6 @@ }, colorThemes: { default: { - positionLeft: '46%', - buttonColor: '#1a1a1a', - buttonBackgroundColor: '#eeeeee', - buttonBorderColor: '#eeeeee', - buttonFocusColor: '#000000', - buttonFocusBackgroundColor: '#dcdcdc', - buttonFocusBorderColor: '#1a1a1a', - menuBackgroundColor: '#eeeeee', - menuBorderColor: '1a1a1a', - menuitemColor: '#1a1a1a', - menuitemBackgroundColor: '#eeeeee', - menuitemFocusColor: '#eeeeee', - menuitemFocusBackgroundColor: '#1a1a1a', - menuitemFocusBorderColor: '#1a1a1a', - }, - illinois: { - positionLeft: '46%', - buttonColor: '#00132c', - buttonBackgroundColor: '#dddede', - buttonBorderColor: '#dddede', - buttonFocusColor: '#00132c', - buttonFocusBackgroundColor: '#cad9ef', - buttonFocusBorderColor: '#ff552e', - menuBackgroundColor: '#cad9ef', - menuitemLevelOpacity: '0.7', - menuBorderColor: '#ff552e', - menuitemColor: '#00132c', - menuitemBackgroundColor: '#cad9ef', - menuitemFocusColor: '#eeeeee', - menuitemFocusBackgroundColor: '#00132c', - menuitemFocusBorderColor: '#ff552e', - }, - aria: { positionLeft: '', buttonColor: '#005a9c;', buttonBackgroundColor: '#def', @@ -158,11 +119,11 @@ }, }, defaultCSS: - '.skip-to.popup{position:absolute;top:-30em;left:-3000em}.skip-to,.skip-to.popup.focus{position:absolute;top:0;left:$positionLeft}.skip-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.skip-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.skip-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.skip-to [role=menuitem] .label:first-letter,.skip-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.skip-to [role=menuitem] .level{text-align:right;padding-right:4px}.skip-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.skip-to [role=menuitem].skip-to-h1 .level{grid-column:1}.skip-to [role=menuitem].skip-to-h2 .level{grid-column:2}.skip-to [role=menuitem].skip-to-h3 .level{grid-column:3}.skip-to [role=menuitem].skip-to-h4 .level{grid-column:4}.skip-to [role=menuitem].skip-to-h5 .level{grid-column:5}.skip-to [role=menuitem].skip-to-h6 .level{grid-column:8}.skip-to [role=menuitem].skip-to-h1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h3 .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h4 .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h5 .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-h6 .label{grid-column:7/8}.skip-to [role=menuitem].skip-to-h1.no-level .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-h2.no-level .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-h3.no-level .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-h4.no-level .label{grid-column:4/8}.skip-to [role=menuitem].skip-to-h5.no-level .label{grid-column:5/8}.skip-to [role=menuitem].skip-to-h6.no-level .label{grid-column:6/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .nesting{grid-column:1}.skip-to [role=menuitem].skip-to-nesting-level-2 .nesting{grid-column:2}.skip-to [role=menuitem].skip-to-nesting-level-3 .nesting{grid-column:3}.skip-to [role=menuitem].skip-to-nesting-level-0 .label{grid-column:1/8}.skip-to [role=menuitem].skip-to-nesting-level-1 .label{grid-column:2/8}.skip-to [role=menuitem].skip-to-nesting-level-2 .label{grid-column:3/8}.skip-to [role=menuitem].skip-to-nesting-level-3 .label{grid-column:4/8}.skip-to [role=menuitem].action .label,.skip-to [role=menuitem].no-items .label{grid-column:1/8}.skip-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.skip-to [role=separator]:first-child{border-radius:5px 5px 0 0}.skip-to [role=menuitem].last{border-radius:0 0 5px 5px}.skip-to.focus{display:block}.skip-to button:focus,.skip-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.skip-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.skip-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', + '.jump-to.popup{position:absolute;top:-30em;left:-3000em}.jump-to,.jump-to.popup.focus{position:absolute;top:0;left:$positionLeft}.jump-to button{position:relative;margin:0;padding:6px 8px 6px 8px;border-width:0 1px 1px 1px;border-style:solid;border-radius:0 0 6px 6px;background-color:$buttonBackgroundColor;border-color:$buttonBorderColor;color:$buttonColor;z-index:1000}.jump-to [role=menu]{position:absolute;min-width:17em;display:none;margin:0;padding:.25rem;background-color:$menuBackgroundColor;border-width:2px;border-style:solid;border-color:$menuBorderColor;border-radius:5px;z-index:1000}.jump-to [role=group]{display:grid;grid-auto-rows:min-content;grid-row-gap:1px}.jump-to [role=separator]:first-child{border-radius:5px 5px 0 0}.jump-to [role=menuitem]{padding:3px;display:block;width:auto;border-width:0;border-style:solid;color:$menuitemColor;background-color:$menuitemBackgroundColor;z-index:1000;display:grid;overflow-y:auto;grid-template-columns:repeat(6,1.2rem) 1fr;grid-column-gap:2px;font-size:1em}.jump-to [role=menuitem] .label:first-letter,.jump-to [role=menuitem] .level:first-letter{text-decoration:underline;text-transform:uppercase}.jump-to [role=menuitem] .level{text-align:right;padding-right:4px}.jump-to [role=menuitem] .label{margin:0;padding:0;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jump-to [role=menuitem].jump-to-h1 .level{grid-column:1}.jump-to [role=menuitem].jump-to-h2 .level{grid-column:2}.jump-to [role=menuitem].jump-to-h3 .level{grid-column:3}.jump-to [role=menuitem].jump-to-h4 .level{grid-column:4}.jump-to [role=menuitem].jump-to-h5 .level{grid-column:5}.jump-to [role=menuitem].jump-to-h6 .level{grid-column:8}.jump-to [role=menuitem].jump-to-h1 .label{grid-column:2/8}.jump-to [role=menuitem].jump-to-h2 .label{grid-column:3/8}.jump-to [role=menuitem].jump-to-h3 .label{grid-column:4/8}.jump-to [role=menuitem].jump-to-h4 .label{grid-column:5/8}.jump-to [role=menuitem].jump-to-h5 .label{grid-column:6/8}.jump-to [role=menuitem].jump-to-h6 .label{grid-column:7/8}.jump-to [role=menuitem].jump-to-h1.no-level .label{grid-column:1/8}.jump-to [role=menuitem].jump-to-h2.no-level .label{grid-column:2/8}.jump-to [role=menuitem].jump-to-h3.no-level .label{grid-column:3/8}.jump-to [role=menuitem].jump-to-h4.no-level .label{grid-column:4/8}.jump-to [role=menuitem].jump-to-h5.no-level .label{grid-column:5/8}.jump-to [role=menuitem].jump-to-h6.no-level .label{grid-column:6/8}.jump-to [role=menuitem].jump-to-nesting-level-1 .nesting{grid-column:1}.jump-to [role=menuitem].jump-to-nesting-level-2 .nesting{grid-column:2}.jump-to [role=menuitem].jump-to-nesting-level-3 .nesting{grid-column:3}.jump-to [role=menuitem].jump-to-nesting-level-0 .label{grid-column:1/8}.jump-to [role=menuitem].jump-to-nesting-level-1 .label{grid-column:2/8}.jump-to [role=menuitem].jump-to-nesting-level-2 .label{grid-column:3/8}.jump-to [role=menuitem].jump-to-nesting-level-3 .label{grid-column:4/8}.jump-to [role=menuitem].action .label,.jump-to [role=menuitem].no-items .label{grid-column:1/8}.jump-to [role=separator]{margin:1px 0 1px 0;padding:3px;display:block;width:auto;font-weight:700;text-align:left;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:$menuitemColor;background-color:$menuitemBackgroundColor;color:$menuitemColor;z-index:1000}.jump-to [role=separator]:first-child{border-radius:5px 5px 0 0}.jump-to [role=menuitem].last{border-radius:0 0 5px 5px}.jump-to.focus{display:block}.jump-to button:focus,.jump-to button:hover{background-color:$buttonFocusBackgroundColor;color:$buttonFocusColor;outline:0}.jump-to button:focus{padding:4px 7px 5px 7px;border-width:2px 2px 2px 2px;border-color:$buttonFocusBorderColor}.jump-to [role=menuitem]:focus{padding:1px;border-width:2px;border-style:solid;border-color:$menuitemFocusBorderColor;background-color:$menuitemFocusBackgroundColor;color:$menuitemFocusColor;outline:0}', // // Functions related to configuring the features - // of skipTo + // of JumpTo // updateStyle: function (stylePlaceholder, value, defaultValue) { @@ -302,7 +263,7 @@ this.addCSSColors(); this.renderStyleElement(this.defaultCSS); this.domNode = document.createElement(this.config.containerElement); - this.domNode.classList.add('skip-to'); + this.domNode.classList.add('jump-to'); if (this.isNotEmptyString(this.config.customClass)) { this.domNode.classList.add(this.config.customClass); } @@ -391,7 +352,7 @@ localConfig[name] = appConfigSettings[name]; } else { console.log( - '** SkipTo Problem with user configuration option "' + name + '".' + '** JumpTo Problem with user configuration option "' + name + '".' ); } } @@ -497,14 +458,14 @@ } menuitemNode.setAttribute('data-level', mi.level); if (mi.tagName && mi.tagName.length) { - menuitemNode.classList.add('skip-to-' + mi.tagName); + menuitemNode.classList.add('jump-to-' + mi.tagName); } } // add nesting level for landmarks if (mi.class.includes('landmark')) { menuitemNode.setAttribute('data-nesting', mi.nestingLevel); - menuitemNode.classList.add('skip-to-nesting-level-' + mi.nestingLevel); + menuitemNode.classList.add('jump-to-nesting-level-' + mi.nestingLevel); if (mi.nestingLevel > 0 && mi.nestingLevel > this.lastNestingLevel) { nestingNode = document.createElement('span'); @@ -563,7 +524,7 @@ if (option === 'all') { return this.config.headingAllGroupLabel; } - return this.config.headingImportantGroupLabel; + return this.config.headingSelectedGroupLabel; }, getShowMoreHeadingsSelector: function (option) { @@ -576,7 +537,7 @@ getShowMoreHeadingsLabel: function (option) { var label, n; - label = this.config.actionShowImportantHeadingsLabel; + label = this.config.actionShowSelectedHeadingsLabel; if (option === 'all') { label = this.config.actionShowAllHeadingsLabel; @@ -594,7 +555,7 @@ getShowMoreHeadingsAriaLabel: function (option) { var label, n; - label = this.config.actionShowImportantHeadingsAriaLabel; + label = this.config.actionShowSelectedHeadingsAriaLabel; if (option === 'all') { label = this.config.actionShowAllHeadingsAriaLabel; @@ -616,7 +577,7 @@ item.tagName = 'action'; item.role = 'menuitem'; item.class = 'action'; - item.dataId = 'skip-to-more-headings'; + item.dataId = 'jump-to-more-headings'; var menuitemNode = this.renderMenuitemToGroup(groupNode, item); menuitemNode.setAttribute('data-show-heading-option', 'all'); menuitemNode.title = this.config.actionShowHeadingsHelp; @@ -625,7 +586,7 @@ updateHeadingGroupMenuitems: function (option) { var selector = this.getShowMoreHeadingsSelector(option); var headings = this.getHeadings(selector); - var groupNode = document.getElementById('id-skip-to-group-headings'); + var groupNode = document.getElementById('id-jump-to-group-headings'); this.renderMenuitemsToGroup( groupNode, headings, @@ -639,18 +600,18 @@ } var labelNode = this.menuNode.querySelector( - '#id-skip-to-group-headings-label' + '#id-jump-to-group-headings-label' ); labelNode.textContent = this.getHeadingsGroupLabel(option); if (option === 'all') { - option = 'important'; + option = 'selected'; } else { option = 'all'; } var menuitemNode = this.menuNode.querySelector( - '[data-id=skip-to-more-headings]' + '[data-id=jump-to-more-headings]' ); menuitemNode.setAttribute('data-show-heading-option', option); menuitemNode.setAttribute( @@ -666,7 +627,7 @@ if (option === 'all') { return this.config.landmarkAllGroupLabel; } - return this.config.landmarkImportantGroupLabel; + return this.config.landmarkSelectedGroupLabel; }, getShowMoreLandmarksSelector: function (option) { @@ -682,7 +643,7 @@ if (option === 'all') { label = this.config.actionShowAllLandmarksLabel; } else { - label = this.config.actionShowImportantLandmarksLabel; + label = this.config.actionShowSelectedLandmarksLabel; } n = this.getLandmarks(this.getShowMoreLandmarksSelector(option)); @@ -701,7 +662,7 @@ if (option === 'all') { label = this.config.actionShowAllLandmarksAriaLabel; } else { - label = this.config.actionShowImportantLandmarksAriaLabel; + label = this.config.actionShowSelectedLandmarksAriaLabel; } n = this.getLandmarks(this.getShowMoreLandmarksSelector(option)); @@ -721,7 +682,7 @@ item.tagName = 'action'; item.role = 'menuitem'; item.class = 'action'; - item.dataId = 'skip-to-more-landmarks'; + item.dataId = 'jump-to-more-landmarks'; var menuitemNode = this.renderMenuitemToGroup(groupNode, item); menuitemNode.setAttribute('data-show-landmark-option', 'all'); menuitemNode.title = this.config.actionShowLandmarksHelp; @@ -730,7 +691,7 @@ updateLandmarksGroupMenuitems: function (option) { var selector = this.getShowMoreLandmarksSelector(option); var landmarks = this.getLandmarks(selector, option === 'all'); - var groupNode = document.getElementById('id-skip-to-group-landmarks'); + var groupNode = document.getElementById('id-jump-to-group-landmarks'); this.renderMenuitemsToGroup( groupNode, landmarks, @@ -744,18 +705,18 @@ } var labelNode = this.menuNode.querySelector( - '#id-skip-to-group-landmarks-label' + '#id-jump-to-group-landmarks-label' ); labelNode.textContent = this.getLandmarksGroupLabel(option); if (option === 'all') { - option = 'important'; + option = 'selected'; } else { option = 'all'; } var menuitemNode = this.menuNode.querySelector( - '[data-id=skip-to-more-landmarks]' + '[data-id=jump-to-more-landmarks]' ); menuitemNode.setAttribute('data-show-landmark-option', option); menuitemNode.setAttribute( @@ -777,8 +738,8 @@ // Create landmarks group landmarkElements = this.getLandmarks(); groupNode = this.renderMenuitemGroup( - 'id-skip-to-group-landmarks', - this.config.landmarkImportantGroupLabel + 'id-jump-to-group-landmarks', + this.config.landmarkSelectedGroupLabel ); this.renderMenuitemsToGroup( groupNode, @@ -789,8 +750,8 @@ // Create headings group headingElements = this.getHeadings(); groupNode = this.renderMenuitemGroup( - 'id-skip-to-group-headings', - this.config.headingImportantGroupLabel + 'id-jump-to-group-headings', + this.config.headingSelectedGroupLabel ); this.renderMenuitemsToGroup( groupNode, @@ -801,11 +762,11 @@ // Create actions, if enabled if (this.config.enableActions) { groupNode = this.renderMenuitemGroup( - 'id-skip-to-group-actions', + 'id-jump-to-group-actions', this.config.actionGroupLabel ); - this.renderActionMoreHeadings(groupNode); this.renderActionMoreLandmarks(groupNode); + this.renderActionMoreHeadings(groupNode); } // Update list of menuitems @@ -968,9 +929,9 @@ }, skipToElement: function (menuitem) { var inputNode = false; - var isSearch = menuitem.classList.contains('skip-to-search'); + var isSearch = menuitem.classList.contains('jump-to-search'); var node = document.querySelector( - '[data-skip-to-id="' + menuitem.getAttribute('data-id') + '"]' + '[data-jump-to-id="' + menuitem.getAttribute('data-id') + '"]' ); if (node) { if (isSearch) { @@ -992,12 +953,12 @@ // this means there were no headings or landmarks in the list break; - case 'skip-to-more-headings': + case 'jump-to-more-headings': option = tgt.getAttribute('data-show-heading-option'); this.updateHeadingGroupMenuitems(option); break; - case 'skip-to-more-landmarks': + case 'jump-to-more-landmarks': option = tgt.getAttribute('data-show-landmark-option'); this.updateLandmarksGroupMenuitems(option); break; @@ -1189,10 +1150,10 @@ var role = heading.getAttribute('role'); if (typeof role === 'string' && role === 'presentation') continue; if (this.isVisible(heading)) { - if (heading.hasAttribute('data-skip-to-id')) { - dataId = heading.getAttribute('data-skip-to-id'); + if (heading.hasAttribute('data-jump-to-id')) { + dataId = heading.getAttribute('data-jump-to-id'); } else { - heading.setAttribute('data-skip-to-id', this.skipToIdIndex); + heading.setAttribute('data-jump-to-id', this.skipToIdIndex); dataId = this.skipToIdIndex; } level = heading.tagName.substring(1); @@ -1345,10 +1306,10 @@ if (landmark.hasAttribute('aria-roledescription')) { tagName = landmark.getAttribute('aria-roledescription'); } - if (landmark.hasAttribute('data-skip-to-id')) { - dataId = landmark.getAttribute('data-skip-to-id'); + if (landmark.hasAttribute('data-jump-to-id')) { + dataId = landmark.getAttribute('data-jump-to-id'); } else { - landmark.setAttribute('data-skip-to-id', this.skipToIdIndex); + landmark.setAttribute('data-jump-to-id', this.skipToIdIndex); dataId = this.skipToIdIndex; } var landmarkItem = {}; @@ -1406,18 +1367,8 @@ }, }; - // Initialize skipto menu button with onload event - var SkipToConfig = { - settings: { - skipTo: { - colorTheme: 'aria', - }, - }, - }; - window.addEventListener('load', function () { - SkipTo.init(SkipToConfig); - console.log('SkipTo loaded...'); + JumpTo.init(); + console.log('JumpTo loaded...'); }); })(); -/*@end @*/ diff --git a/examples/menu-button/menu-button-actions-active-descendant.html b/examples/menu-button/menu-button-actions-active-descendant.html index 6ea2105b93..1f1d1544ae 100644 --- a/examples/menu-button/menu-button-actions-active-descendant.html +++ b/examples/menu-button/menu-button-actions-active-descendant.html @@ -9,7 +9,7 @@ - + diff --git a/examples/menu-button/menu-button-actions.html b/examples/menu-button/menu-button-actions.html index acdf175037..884337b2d6 100644 --- a/examples/menu-button/menu-button-actions.html +++ b/examples/menu-button/menu-button-actions.html @@ -9,7 +9,7 @@ - + diff --git a/examples/menu-button/menu-button-links.html b/examples/menu-button/menu-button-links.html index 370c86db2d..9d56ebcb9c 100644 --- a/examples/menu-button/menu-button-links.html +++ b/examples/menu-button/menu-button-links.html @@ -10,7 +10,7 @@ - + diff --git a/examples/radio/radio-activedescendant.html b/examples/radio/radio-activedescendant.html index ae52dcc57f..cd5b3d931f 100644 --- a/examples/radio/radio-activedescendant.html +++ b/examples/radio/radio-activedescendant.html @@ -10,7 +10,7 @@ - + diff --git a/examples/radio/radio.html b/examples/radio/radio.html index ec667c0219..aad3517a9f 100644 --- a/examples/radio/radio.html +++ b/examples/radio/radio.html @@ -10,7 +10,7 @@ - + From b6c475ef6ff319e4718936efe1eea9534904de83 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 23 Dec 2020 09:44:08 -0600 Subject: [PATCH 14/15] updated JumpTo and added item to spell configuration --- cspell.json | 1 + examples/js/jumpto.js | 51 +++++++++++-------------------------------- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/cspell.json b/cspell.json index b4eabcb94c..914f36e268 100644 --- a/cspell.json +++ b/cspell.json @@ -85,6 +85,7 @@ "jarosewli", "Jèrôme", "Jinyuan", + "JumpTo", "Kasper", "kbdshortcuts", "Kowno", diff --git a/examples/js/jumpto.js b/examples/js/jumpto.js index 565855f53a..8f3e6fb33f 100644 --- a/examples/js/jumpto.js +++ b/examples/js/jumpto.js @@ -6,6 +6,7 @@ * * Desc: Jump to provides keyboard navigation to document structure * This feature is based on the ARIA APG menu button example + * NOTE: This code has been contributed to the SkipTo project */ 'use strict'; @@ -20,7 +21,7 @@ lastMenuitem: false, firstChars: [], headingLevels: [], - skipToIdIndex: 1, + JumpToIdIndex: 1, showAllLandmarksSelector: 'main, [role=main], [role=search], nav, [role=navigation], section[aria-label], section[aria-labelledby], section[title], [role=region][aria-label], [role=region][aria-labelledby], [role=region][title], form[aria-label], form[aria-labelledby], aside, [role=complementary], body > header, [role=banner], body > footer, [role=contentinfo]', showAllHeadingsSelector: 'h1, h2, h3, h4, h5, h6', @@ -249,11 +250,8 @@ return accesskey + this.config.accesskeyNotSupported; }, - init: function (config) { + init: function () { var attachElement = document.body; - if (config) { - this.setUpConfig(config); - } if (typeof this.config.attachElement === 'string') { var node = document.querySelector(this.config.attachElement); if (node && node.nodeType === Node.ELEMENT_NODE) { @@ -284,7 +282,7 @@ } } } - // Place skip to at the beginning of the document + // Place jump to at the beginning of the document if (attachElement.firstElementChild) { attachElement.insertBefore( this.domNode, @@ -334,29 +332,6 @@ true ); }, - setUpConfig: function (appConfig) { - var localConfig = this.config, - name, - appConfigSettings = - typeof appConfig.settings !== 'undefined' - ? appConfig.settings.skipTo - : {}; - for (name in appConfigSettings) { - //overwrite values of our local config, based on the external config - if ( - typeof localConfig[name] !== 'undefined' && - ((typeof appConfigSettings[name] === 'string' && - appConfigSettings[name].length > 0) || - typeof appConfigSettings[name] === 'boolean') - ) { - localConfig[name] = appConfigSettings[name]; - } else { - console.log( - '** JumpTo Problem with user configuration option "' + name + '".' - ); - } - } - }, renderStyleElement: function (cssString) { var styleNode = document.createElement('style'); var headNode = document.getElementsByTagName('head')[0]; @@ -927,7 +902,7 @@ event.stopPropagation(); event.preventDefault(); }, - skipToElement: function (menuitem) { + JumpToElement: function (menuitem) { var inputNode = false; var isSearch = menuitem.classList.contains('jump-to-search'); var node = document.querySelector( @@ -965,7 +940,7 @@ default: this.closePopup(); - this.skipToElement(tgt); + this.JumpToElement(tgt); break; } }, @@ -1153,8 +1128,8 @@ if (heading.hasAttribute('data-jump-to-id')) { dataId = heading.getAttribute('data-jump-to-id'); } else { - heading.setAttribute('data-jump-to-id', this.skipToIdIndex); - dataId = this.skipToIdIndex; + heading.setAttribute('data-jump-to-id', this.JumpToIdIndex); + dataId = this.JumpToIdIndex; } level = heading.tagName.substring(1); var headingItem = {}; @@ -1167,7 +1142,7 @@ headingItem.role = 'heading'; headingItem.level = level; headingElementsArr.push(headingItem); - this.skipToIdIndex += 1; + this.JumpToIdIndex += 1; } } return headingElementsArr; @@ -1246,7 +1221,7 @@ var dataId = ''; for (var i = 0, len = landmarks.length; i < len; i += 1) { var landmark = landmarks[i]; - // if skipto is a landmark don't include it in the list + // if JumpTo is a landmark don't include it in the list if (landmark === this.domNode) { continue; } @@ -1309,8 +1284,8 @@ if (landmark.hasAttribute('data-jump-to-id')) { dataId = landmark.getAttribute('data-jump-to-id'); } else { - landmark.setAttribute('data-jump-to-id', this.skipToIdIndex); - dataId = this.skipToIdIndex; + landmark.setAttribute('data-jump-to-id', this.JumpToIdIndex); + dataId = this.JumpToIdIndex; } var landmarkItem = {}; landmarkItem.dataId = dataId.toString(); @@ -1324,7 +1299,7 @@ landmarks ); } - this.skipToIdIndex += 1; + this.JumpToIdIndex += 1; allLandmarks.push(landmarkItem); // For sorting landmarks into groups switch (tagName) { From 92c897dc4a0d3d876b600cdfecc371775079ad28 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 23 Dec 2020 09:48:58 -0600 Subject: [PATCH 15/15] removed console statement --- examples/js/jumpto.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/js/jumpto.js b/examples/js/jumpto.js index 8f3e6fb33f..4705066ac1 100644 --- a/examples/js/jumpto.js +++ b/examples/js/jumpto.js @@ -1344,6 +1344,5 @@ window.addEventListener('load', function () { JumpTo.init(); - console.log('JumpTo loaded...'); }); })();