diff --git a/content/patterns/listbox/examples/css/listbox.css b/content/patterns/listbox/examples/css/listbox.css index 7fc920c5..5ecb8d12 100644 --- a/content/patterns/listbox/examples/css/listbox.css +++ b/content/patterns/listbox/examples/css/listbox.css @@ -44,15 +44,30 @@ [role="option"] { position: relative; display: block; - padding: 0 1em 0 1.5em; + margin: 2px; + padding: 2px 1em 2px 1.5em; line-height: 1.8em; + cursor: pointer; } -[role="option"].focused { +[role="listbox"]:focus [role="option"].focused { background: #bde4ff; } -[role="option"][aria-selected="true"]::before { +[role="listbox"]:focus [role="option"].focused, +[role="option"]:hover { + outline: 2px solid currentcolor; +} + +.move-right-btn span.checkmark::after { + content: " →"; +} + +.move-left-btn span.checkmark::before { + content: "← "; +} + +[role="option"][aria-selected="true"] span.checkmark::before { position: absolute; left: 0.5em; content: "✓"; @@ -120,14 +135,6 @@ button[aria-disabled="true"] { opacity: 0.5; } -.move-right-btn::after { - content: " →"; -} - -.move-left-btn::before { - content: "← "; -} - .annotate { color: #366ed4; font-style: italic; diff --git a/content/patterns/listbox/examples/js/listbox-collapsible.js b/content/patterns/listbox/examples/js/listbox-collapsible.js index 4c5733de..4beaae9f 100644 --- a/content/patterns/listbox/examples/js/listbox-collapsible.js +++ b/content/patterns/listbox/examples/js/listbox-collapsible.js @@ -1,4 +1,18 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + */ + 'use strict'; + +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Collapsible Dropdown Listbox Example * @@ -7,70 +21,66 @@ */ window.addEventListener('load', function () { - var button = document.getElementById('exp_button'); - var exListbox = new aria.Listbox(document.getElementById('exp_elem_list')); - new aria.ListboxButton(button, exListbox); + const button = document.getElementById('exp_button'); + const exListbox = new aria.Listbox(document.getElementById('exp_elem_list')); + new ListboxButton(button, exListbox); }); -var aria = aria || {}; - -aria.ListboxButton = function (button, listbox) { - this.button = button; - this.listbox = listbox; - this.registerEvents(); -}; - -aria.ListboxButton.prototype.registerEvents = function () { - this.button.addEventListener('click', this.showListbox.bind(this)); - this.button.addEventListener('keyup', this.checkShow.bind(this)); - this.listbox.listboxNode.addEventListener( - 'blur', - this.hideListbox.bind(this) - ); - this.listbox.listboxNode.addEventListener( - 'keydown', - this.checkHide.bind(this) - ); - this.listbox.setHandleFocusChange(this.onFocusChange.bind(this)); -}; - -aria.ListboxButton.prototype.checkShow = function (evt) { - var key = evt.which || evt.keyCode; +class ListboxButton { + constructor(button, listbox) { + this.button = button; + this.listbox = listbox; + this.registerEvents(); + } - switch (key) { - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - evt.preventDefault(); - this.showListbox(); - this.listbox.checkKeyPress(evt); - break; + registerEvents() { + this.button.addEventListener('click', this.showListbox.bind(this)); + this.button.addEventListener('keyup', this.checkShow.bind(this)); + this.listbox.listboxNode.addEventListener( + 'blur', + this.hideListbox.bind(this) + ); + this.listbox.listboxNode.addEventListener( + 'keydown', + this.checkHide.bind(this) + ); + this.listbox.setHandleFocusChange(this.onFocusChange.bind(this)); } -}; -aria.ListboxButton.prototype.checkHide = function (evt) { - var key = evt.which || evt.keyCode; + checkShow(evt) { + switch (evt.key) { + case 'ArrowUp': + case 'ArrowDown': + evt.preventDefault(); + this.showListbox(); + this.listbox.checkKeyPress(evt); + break; + } + } - switch (key) { - case aria.KeyCode.RETURN: - case aria.KeyCode.ESC: - evt.preventDefault(); - this.hideListbox(); - this.button.focus(); - break; + checkHide(evt) { + switch (evt.key) { + case 'Enter': + case 'Escape': + evt.preventDefault(); + this.hideListbox(); + this.button.focus(); + break; + } } -}; -aria.ListboxButton.prototype.showListbox = function () { - aria.Utils.removeClass(this.listbox.listboxNode, 'hidden'); - this.button.setAttribute('aria-expanded', 'true'); - this.listbox.listboxNode.focus(); -}; + showListbox() { + this.listbox.listboxNode.classList.remove('hidden'); + this.button.setAttribute('aria-expanded', 'true'); + this.listbox.listboxNode.focus(); + } -aria.ListboxButton.prototype.hideListbox = function () { - aria.Utils.addClass(this.listbox.listboxNode, 'hidden'); - this.button.removeAttribute('aria-expanded'); -}; + hideListbox() { + this.listbox.listboxNode.classList.add('hidden'); + this.button.removeAttribute('aria-expanded'); + } -aria.ListboxButton.prototype.onFocusChange = function (focusedItem) { - this.button.innerText = focusedItem.innerText; -}; + onFocusChange(focusedItem) { + this.button.innerText = focusedItem.innerText; + } +} diff --git a/content/patterns/listbox/examples/js/listbox-rearrangeable.js b/content/patterns/listbox/examples/js/listbox-rearrangeable.js index 561b5d51..b5ba8ee8 100644 --- a/content/patterns/listbox/examples/js/listbox-rearrangeable.js +++ b/content/patterns/listbox/examples/js/listbox-rearrangeable.js @@ -3,10 +3,16 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Listbox Examples * diff --git a/content/patterns/listbox/examples/js/listbox-scrollable.js b/content/patterns/listbox/examples/js/listbox-scrollable.js index 40204060..fe7b2658 100644 --- a/content/patterns/listbox/examples/js/listbox-scrollable.js +++ b/content/patterns/listbox/examples/js/listbox-scrollable.js @@ -3,10 +3,16 @@ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document */ -/* global aria */ - 'use strict'; +/** + * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes + */ +var aria = aria || {}; + /** * ARIA Scrollable Listbox Example * diff --git a/content/patterns/listbox/examples/js/listbox.js b/content/patterns/listbox/examples/js/listbox.js index 8a24176b..632063f2 100644 --- a/content/patterns/listbox/examples/js/listbox.js +++ b/content/patterns/listbox/examples/js/listbox.js @@ -7,6 +7,9 @@ /** * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes */ var aria = aria || {}; @@ -17,681 +20,668 @@ var aria = aria || {}; * @param listboxNode * The DOM node pointing to the listbox */ -aria.Listbox = function (listboxNode) { - this.listboxNode = listboxNode; - this.activeDescendant = this.listboxNode.getAttribute( - 'aria-activedescendant' - ); - this.multiselectable = this.listboxNode.hasAttribute('aria-multiselectable'); - this.moveUpDownEnabled = false; - this.siblingList = null; - this.startRangeIndex = 0; - this.upButton = null; - this.downButton = null; - this.moveButton = null; - this.keysSoFar = ''; - this.handleFocusChange = function () {}; - this.handleItemChange = function () {}; - this.registerEvents(); -}; -/** - * @description - * Register events for the listbox interactions - */ -aria.Listbox.prototype.registerEvents = function () { - this.listboxNode.addEventListener('focus', this.setupFocus.bind(this)); - this.listboxNode.addEventListener('keydown', this.checkKeyPress.bind(this)); - this.listboxNode.addEventListener('click', this.checkClickItem.bind(this)); - - if (this.multiselectable) { - this.listboxNode.addEventListener( - 'mousedown', - this.checkMouseDown.bind(this) +aria.Listbox = class Listbox { + constructor(listboxNode) { + this.listboxNode = listboxNode; + this.activeDescendant = this.listboxNode.getAttribute( + 'aria-activedescendant' + ); + this.multiselectable = this.listboxNode.hasAttribute( + 'aria-multiselectable' ); + this.moveUpDownEnabled = false; + this.siblingList = null; + this.startRangeIndex = 0; + this.upButton = null; + this.downButton = null; + this.moveButton = null; + this.keysSoFar = ''; + this.handleFocusChange = function () {}; + this.handleItemChange = function () {}; + this.registerEvents(); + } + + registerEvents() { + this.listboxNode.addEventListener('focus', this.setupFocus.bind(this)); + this.listboxNode.addEventListener('keydown', this.checkKeyPress.bind(this)); + this.listboxNode.addEventListener('click', this.checkClickItem.bind(this)); + + if (this.multiselectable) { + this.listboxNode.addEventListener( + 'mousedown', + this.checkMouseDown.bind(this) + ); + } } -}; -/** - * @description - * If there is no activeDescendant, focus on the first option - */ -aria.Listbox.prototype.setupFocus = function () { - if (this.activeDescendant) { - return; + setupFocus() { + if (this.activeDescendant) { + const listitem = document.getElementById(this.activeDescendant); + listitem.scrollIntoView({ block: 'nearest', inline: 'nearest' }); + } } -}; -/** - * @description - * Focus on the first option - */ -aria.Listbox.prototype.focusFirstItem = function () { - var firstItem = this.listboxNode.querySelector('[role="option"]'); + focusFirstItem() { + var firstItem = this.listboxNode.querySelector('[role="option"]'); - if (firstItem) { - this.focusItem(firstItem); + if (firstItem) { + this.focusItem(firstItem); + } } -}; -/** - * @description - * Focus on the last option - */ -aria.Listbox.prototype.focusLastItem = function () { - var itemList = this.listboxNode.querySelectorAll('[role="option"]'); + focusLastItem() { + const itemList = this.listboxNode.querySelectorAll('[role="option"]'); - if (itemList.length) { - this.focusItem(itemList[itemList.length - 1]); + if (itemList.length) { + this.focusItem(itemList[itemList.length - 1]); + } } -}; -/** - * @description - * Handle various keyboard controls; UP/DOWN will shift focus; SPACE selects - * an item. - * @param evt - * The keydown event object - */ -aria.Listbox.prototype.checkKeyPress = function (evt) { - var key = evt.which || evt.keyCode; - var lastActiveId = this.activeDescendant; - var allOptions = this.listboxNode.querySelectorAll('[role="option"]'); - var currentItem = - document.getElementById(this.activeDescendant) || allOptions[0]; - var nextItem = currentItem; - - if (!currentItem) { - return; - } - - switch (key) { - case aria.KeyCode.PAGE_UP: - case aria.KeyCode.PAGE_DOWN: - if (this.moveUpDownEnabled) { - evt.preventDefault(); + checkKeyPress(evt) { + const lastActiveId = this.activeDescendant; + const allOptions = this.listboxNode.querySelectorAll('[role="option"]'); + const currentItem = + document.getElementById(this.activeDescendant) || allOptions[0]; + let nextItem = currentItem; - if (key === aria.KeyCode.PAGE_UP) { - this.moveUpItems(); - } else { - this.moveDownItems(); + if (!currentItem) { + return; + } + + switch (evt.key) { + case 'PageUp': + case 'PageDown': + evt.preventDefault(); + if (this.moveUpDownEnabled) { + if (evt.key === 'PageUp') { + this.moveUpItems(); + } else { + this.moveDownItems(); + } } - } - break; - case aria.KeyCode.UP: - case aria.KeyCode.DOWN: - if (!this.activeDescendant) { - // focus first option if no option was previously focused, and perform no other actions - this.focusItem(currentItem); break; - } - - if (this.moveUpDownEnabled && evt.altKey) { + case 'ArrowUp': + case 'ArrowDown': evt.preventDefault(); - if (key === aria.KeyCode.UP) { - this.moveUpItems(); + if (!this.activeDescendant) { + // focus first option if no option was previously focused, and perform no other actions + this.focusItem(currentItem); + break; + } + + if (this.moveUpDownEnabled && evt.altKey) { + evt.preventDefault(); + if (evt.key === 'ArrowUp') { + this.moveUpItems(); + } else { + this.moveDownItems(); + } + this.updateScroll(); + return; + } + + if (evt.key === 'ArrowUp') { + nextItem = this.findPreviousOption(currentItem); } else { - this.moveDownItems(); + nextItem = this.findNextOption(currentItem); } - return; - } - if (key === aria.KeyCode.UP) { - nextItem = this.findPreviousOption(currentItem); - } else { - nextItem = this.findNextOption(currentItem); - } + if (nextItem && this.multiselectable && event.shiftKey) { + this.selectRange(this.startRangeIndex, nextItem); + } - if (nextItem && this.multiselectable && event.shiftKey) { - this.selectRange(this.startRangeIndex, nextItem); - } + if (nextItem) { + this.focusItem(nextItem); + } - if (nextItem) { - this.focusItem(nextItem); + break; + + case 'Home': evt.preventDefault(); - } + this.focusFirstItem(); - break; - case aria.KeyCode.HOME: - evt.preventDefault(); - this.focusFirstItem(); + if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { + this.selectRange(this.startRangeIndex, 0); + } + break; - if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { - this.selectRange(this.startRangeIndex, 0); - } - break; - case aria.KeyCode.END: - evt.preventDefault(); - this.focusLastItem(); + case 'End': + evt.preventDefault(); + this.focusLastItem(); - if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { - this.selectRange(this.startRangeIndex, allOptions.length - 1); - } - break; - case aria.KeyCode.SHIFT: - this.startRangeIndex = this.getElementIndex(currentItem, allOptions); - break; - case aria.KeyCode.SPACE: - evt.preventDefault(); - this.toggleSelectItem(nextItem); - break; - case aria.KeyCode.BACKSPACE: - case aria.KeyCode.DELETE: - case aria.KeyCode.RETURN: - if (!this.moveButton) { - return; - } + if (this.multiselectable && evt.shiftKey && evt.ctrlKey) { + this.selectRange(this.startRangeIndex, allOptions.length - 1); + } + break; - var keyshortcuts = this.moveButton.getAttribute('aria-keyshortcuts'); - if (key === aria.KeyCode.RETURN && keyshortcuts.indexOf('Enter') === -1) { - return; - } - if ( - (key === aria.KeyCode.BACKSPACE || key === aria.KeyCode.DELETE) && - keyshortcuts.indexOf('Delete') === -1 - ) { - return; - } + case 'Shift': + this.startRangeIndex = this.getElementIndex(currentItem, allOptions); + break; - evt.preventDefault(); + case ' ': + evt.preventDefault(); + this.toggleSelectItem(nextItem); + break; - var nextUnselected = nextItem.nextElementSibling; - while (nextUnselected) { - if (nextUnselected.getAttribute('aria-selected') != 'true') { - break; + case 'Backspace': + case 'Delete': + case 'Enter': + if (!this.moveButton) { + return; } - nextUnselected = nextUnselected.nextElementSibling; - } - if (!nextUnselected) { - nextUnselected = nextItem.previousElementSibling; + + var keyshortcuts = this.moveButton.getAttribute('aria-keyshortcuts'); + if (evt.key === 'Enter' && keyshortcuts.indexOf('Enter') === -1) { + return; + } + if ( + (evt.key === 'Backspace' || evt.key === 'Delete') && + keyshortcuts.indexOf('Delete') === -1 + ) { + return; + } + + evt.preventDefault(); + + var nextUnselected = nextItem.nextElementSibling; while (nextUnselected) { if (nextUnselected.getAttribute('aria-selected') != 'true') { break; } - nextUnselected = nextUnselected.previousElementSibling; + nextUnselected = nextUnselected.nextElementSibling; + } + if (!nextUnselected) { + nextUnselected = nextItem.previousElementSibling; + while (nextUnselected) { + if (nextUnselected.getAttribute('aria-selected') != 'true') { + break; + } + nextUnselected = nextUnselected.previousElementSibling; + } } - } - this.moveItems(); + this.moveItems(); - if (!this.activeDescendant && nextUnselected) { - this.focusItem(nextUnselected); - } - break; - case 65: - // handle control + A - if (this.multiselectable && (evt.ctrlKey || evt.metaKey)) { - evt.preventDefault(); - this.selectRange(0, allOptions.length - 1); + if (!this.activeDescendant && nextUnselected) { + this.focusItem(nextUnselected); + } break; - } - // fall through - default: - var itemToFocus = this.findItemToFocus(key); - if (itemToFocus) { - this.focusItem(itemToFocus); - } - break; - } - if (this.activeDescendant !== lastActiveId) { - this.updateScroll(); + case 'A': + case 'a': + // handle control + A + if (evt.ctrlKey || evt.metaKey) { + if (this.multiselectable) { + this.selectRange(0, allOptions.length - 1); + } + evt.preventDefault(); + break; + } + // fall through + default: + if (evt.key.length === 1) { + const itemToFocus = this.findItemToFocus(evt.key.toLowerCase()); + if (itemToFocus) { + this.focusItem(itemToFocus); + } + } + break; + } + + if (this.activeDescendant !== lastActiveId) { + this.updateScroll(); + } } -}; -aria.Listbox.prototype.findItemToFocus = function (key) { - var itemList = this.listboxNode.querySelectorAll('[role="option"]'); - var character = String.fromCharCode(key); - var searchIndex = 0; + findItemToFocus(character) { + const itemList = this.listboxNode.querySelectorAll('[role="option"]'); + let searchIndex = 0; - if (!this.keysSoFar) { - for (var i = 0; i < itemList.length; i++) { - if (itemList[i].getAttribute('id') == this.activeDescendant) { - searchIndex = i; + if (!this.keysSoFar) { + for (let i = 0; i < itemList.length; i++) { + if (itemList[i].getAttribute('id') == this.activeDescendant) { + searchIndex = i; + } } } - } - this.keysSoFar += character; - this.clearKeysSoFarAfterDelay(); - var nextMatch = this.findMatchInRange( - itemList, - searchIndex + 1, - itemList.length - ); - if (!nextMatch) { - nextMatch = this.findMatchInRange(itemList, 0, searchIndex); - } - return nextMatch; -}; + this.keysSoFar += character; + this.clearKeysSoFarAfterDelay(); -/* Return the index of the passed element within the passed array, or null if not found */ -aria.Listbox.prototype.getElementIndex = function (option, options) { - var allOptions = Array.prototype.slice.call(options); // convert to array - var optionIndex = allOptions.indexOf(option); + let nextMatch = this.findMatchInRange( + itemList, + searchIndex + 1, + itemList.length + ); - return typeof optionIndex === 'number' ? optionIndex : null; -}; + if (!nextMatch) { + nextMatch = this.findMatchInRange(itemList, 0, searchIndex); + } + return nextMatch; + } -/* Return the next listbox option, if it exists; otherwise, returns null */ -aria.Listbox.prototype.findNextOption = function (currentOption) { - var allOptions = Array.prototype.slice.call( - this.listboxNode.querySelectorAll('[role="option"]') - ); // get options array - var currentOptionIndex = allOptions.indexOf(currentOption); - var nextOption = null; + /* Return the index of the passed element within the passed array, or null if not found */ + getElementIndex(option, options) { + const allOptions = Array.prototype.slice.call(options); // convert to array + const optionIndex = allOptions.indexOf(option); - if (currentOptionIndex > -1 && currentOptionIndex < allOptions.length - 1) { - nextOption = allOptions[currentOptionIndex + 1]; + return typeof optionIndex === 'number' ? optionIndex : null; } - return nextOption; -}; + /* Return the next listbox option, if it exists; otherwise, returns null */ + findNextOption(currentOption) { + const allOptions = Array.prototype.slice.call( + this.listboxNode.querySelectorAll('[role="option"]') + ); // get options array + const currentOptionIndex = allOptions.indexOf(currentOption); + let nextOption = null; -/* Return the previous listbox option, if it exists; otherwise, returns null */ -aria.Listbox.prototype.findPreviousOption = function (currentOption) { - var allOptions = Array.prototype.slice.call( - this.listboxNode.querySelectorAll('[role="option"]') - ); // get options array - var currentOptionIndex = allOptions.indexOf(currentOption); - var previousOption = null; + if (currentOptionIndex > -1 && currentOptionIndex < allOptions.length - 1) { + nextOption = allOptions[currentOptionIndex + 1]; + } - if (currentOptionIndex > -1 && currentOptionIndex > 0) { - previousOption = allOptions[currentOptionIndex - 1]; + return nextOption; } - return previousOption; -}; + /* Return the previous listbox option, if it exists; otherwise, returns null */ + findPreviousOption(currentOption) { + const allOptions = Array.prototype.slice.call( + this.listboxNode.querySelectorAll('[role="option"]') + ); // get options array + const currentOptionIndex = allOptions.indexOf(currentOption); + let previousOption = null; -aria.Listbox.prototype.clearKeysSoFarAfterDelay = function () { - if (this.keyClear) { - clearTimeout(this.keyClear); - this.keyClear = null; - } - this.keyClear = setTimeout( - function () { - this.keysSoFar = ''; - this.keyClear = null; - }.bind(this), - 500 - ); -}; - -aria.Listbox.prototype.findMatchInRange = function ( - list, - startIndex, - endIndex -) { - // Find the first item starting with the keysSoFar substring, searching in - // the specified range of items - for (var n = startIndex; n < endIndex; n++) { - var label = list[n].innerText; - if (label && label.toUpperCase().indexOf(this.keysSoFar) === 0) { - return list[n]; + if (currentOptionIndex > -1 && currentOptionIndex > 0) { + previousOption = allOptions[currentOptionIndex - 1]; } - } - return null; -}; -/** - * @description - * Check if an item is clicked on. If so, focus on it and select it. - * @param evt - * The click event object - */ -aria.Listbox.prototype.checkClickItem = function (evt) { - if (evt.target.getAttribute('role') !== 'option') { - return; + return previousOption; } - this.focusItem(evt.target); - this.toggleSelectItem(evt.target); - this.updateScroll(); - - if (this.multiselectable && evt.shiftKey) { - this.selectRange(this.startRangeIndex, evt.target); + clearKeysSoFarAfterDelay() { + if (this.keyClear) { + clearTimeout(this.keyClear); + this.keyClear = null; + } + this.keyClear = setTimeout( + function () { + this.keysSoFar = ''; + this.keyClear = null; + }.bind(this), + 500 + ); } -}; -/** - * Prevent text selection on shift + click for multi-select listboxes - * - * @param evt - */ -aria.Listbox.prototype.checkMouseDown = function (evt) { - if ( - this.multiselectable && - evt.shiftKey && - evt.target.getAttribute('role') === 'option' - ) { - evt.preventDefault(); + findMatchInRange(list, startIndex, endIndex) { + // Find the first item starting with the keysSoFar substring, searching in + // the specified range of items + for (let n = startIndex; n < endIndex; n++) { + const label = list[n].innerText; + if (label && label.toLowerCase().indexOf(this.keysSoFar) === 0) { + return list[n]; + } + } + return null; } -}; -/** - * @description - * Toggle the aria-selected value - * @param element - * The element to select - */ -aria.Listbox.prototype.toggleSelectItem = function (element) { - if (this.multiselectable) { - element.setAttribute( - 'aria-selected', - element.getAttribute('aria-selected') === 'true' ? 'false' : 'true' - ); + checkClickItem(evt) { + if (evt.target.getAttribute('role') !== 'option') { + return; + } - this.updateMoveButton(); - } -}; + this.focusItem(evt.target); + this.toggleSelectItem(evt.target); + this.updateScroll(); -/** - * @description - * Defocus the specified item - * @param element - * The element to defocus - */ -aria.Listbox.prototype.defocusItem = function (element) { - if (!element) { - return; - } - if (!this.multiselectable) { - element.removeAttribute('aria-selected'); + if (this.multiselectable && evt.shiftKey) { + this.selectRange(this.startRangeIndex, evt.target); + } } - element.classList.remove('focused'); -}; -/** - * @description - * Focus on the specified item - * @param element - * The element to focus - */ -aria.Listbox.prototype.focusItem = function (element) { - this.defocusItem(document.getElementById(this.activeDescendant)); - if (!this.multiselectable) { - element.setAttribute('aria-selected', 'true'); + /** + * Prevent text selection on shift + click for multi-select listboxes + * + * @param evt + */ + checkMouseDown(evt) { + if ( + this.multiselectable && + evt.shiftKey && + evt.target.getAttribute('role') === 'option' + ) { + evt.preventDefault(); + } } - element.classList.add('focused'); - this.listboxNode.setAttribute('aria-activedescendant', element.id); - this.activeDescendant = element.id; - if (!this.multiselectable) { - this.updateMoveButton(); + /** + * @description + * Toggle the aria-selected value + * @param element + * The element to select + */ + toggleSelectItem(element) { + if (this.multiselectable) { + element.setAttribute( + 'aria-selected', + element.getAttribute('aria-selected') === 'true' ? 'false' : 'true' + ); + + this.updateMoveButton(); + } } - this.checkUpDownButtons(); - this.handleFocusChange(element); -}; - -/** - * Helper function to check if a number is within a range; no side effects. - * - * @param index - * @param start - * @param end - * @returns {boolean} - */ -aria.Listbox.prototype.checkInRange = function (index, start, end) { - var rangeStart = start < end ? start : end; - var rangeEnd = start < end ? end : start; + /** + * @description + * Defocus the specified item + * @param element + * The element to defocus + */ + defocusItem(element) { + if (!element) { + return; + } + if (!this.multiselectable) { + element.removeAttribute('aria-selected'); + } + element.classList.remove('focused'); + } + + /** + * @description + * Focus on the specified item + * @param element + * The element to focus + */ + focusItem(element) { + this.defocusItem(document.getElementById(this.activeDescendant)); + if (!this.multiselectable) { + element.setAttribute('aria-selected', 'true'); + } + element.classList.add('focused'); + this.listboxNode.setAttribute('aria-activedescendant', element.id); + this.activeDescendant = element.id; - return index >= rangeStart && index <= rangeEnd; -}; + if (!this.multiselectable) { + this.updateMoveButton(); + } -/** - * Select a range of options - * - * @param start - * @param end - */ -aria.Listbox.prototype.selectRange = function (start, end) { - // get start/end indices - var allOptions = this.listboxNode.querySelectorAll('[role="option"]'); - var startIndex = - typeof start === 'number' ? start : this.getElementIndex(start, allOptions); - var endIndex = - typeof end === 'number' ? end : this.getElementIndex(end, allOptions); + this.checkUpDownButtons(); + this.handleFocusChange(element); + } + + /** + * Helper function to check if a number is within a range; no side effects. + * + * @param index + * @param start + * @param end + * @returns {boolean} + */ + checkInRange(index, start, end) { + const rangeStart = start < end ? start : end; + const rangeEnd = start < end ? end : start; + + return index >= rangeStart && index <= rangeEnd; + } + + /** + * Select a range of options + * + * @param start + * @param end + */ + selectRange(start, end) { + // get start/end indices + const allOptions = this.listboxNode.querySelectorAll('[role="option"]'); + const startIndex = + typeof start === 'number' + ? start + : this.getElementIndex(start, allOptions); + const endIndex = + typeof end === 'number' ? end : this.getElementIndex(end, allOptions); + + for (let index = 0; index < allOptions.length; index++) { + const selected = this.checkInRange(index, startIndex, endIndex); + allOptions[index].setAttribute('aria-selected', selected + ''); + } - for (var index = 0; index < allOptions.length; index++) { - var selected = this.checkInRange(index, startIndex, endIndex); - allOptions[index].setAttribute('aria-selected', selected + ''); + this.updateMoveButton(); } - this.updateMoveButton(); -}; - -/** - * Check for selected options and update moveButton, if applicable - */ -aria.Listbox.prototype.updateMoveButton = function () { - if (!this.moveButton) { - return; - } + /** + * Check for selected options and update moveButton, if applicable + */ + updateMoveButton() { + if (!this.moveButton) { + return; + } - if (this.listboxNode.querySelector('[aria-selected="true"]')) { - this.moveButton.setAttribute('aria-disabled', 'false'); - } else { - this.moveButton.setAttribute('aria-disabled', 'true'); + if (this.listboxNode.querySelector('[aria-selected="true"]')) { + this.moveButton.setAttribute('aria-disabled', 'false'); + } else { + this.moveButton.setAttribute('aria-disabled', 'true'); + } } -}; -/** - * Check if the selected option is in view, and scroll if not - */ -aria.Listbox.prototype.updateScroll = function () { - var selectedOption = document.getElementById(this.activeDescendant); - if ( - selectedOption && - this.listboxNode.scrollHeight > this.listboxNode.clientHeight - ) { - var scrollBottom = - this.listboxNode.clientHeight + this.listboxNode.scrollTop; - var elementBottom = selectedOption.offsetTop + selectedOption.offsetHeight; - if (elementBottom > scrollBottom) { - this.listboxNode.scrollTop = - elementBottom - this.listboxNode.clientHeight; - } else if (selectedOption.offsetTop < this.listboxNode.scrollTop) { - this.listboxNode.scrollTop = selectedOption.offsetTop; + /** + * Check if the selected option is in view, and scroll if not + */ + updateScroll() { + const selectedOption = document.getElementById(this.activeDescendant); + if (selectedOption) { + const scrollBottom = + this.listboxNode.clientHeight + this.listboxNode.scrollTop; + const elementBottom = + selectedOption.offsetTop + selectedOption.offsetHeight; + if (elementBottom > scrollBottom) { + this.listboxNode.scrollTop = + elementBottom - this.listboxNode.clientHeight; + } else if (selectedOption.offsetTop < this.listboxNode.scrollTop) { + this.listboxNode.scrollTop = selectedOption.offsetTop; + } + selectedOption.scrollIntoView({ block: 'nearest', inline: 'nearest' }); } } -}; - -/** - * @description - * Enable/disable the up/down arrows based on the activeDescendant. - */ -aria.Listbox.prototype.checkUpDownButtons = function () { - var activeElement = document.getElementById(this.activeDescendant); - if (!this.moveUpDownEnabled) { - return; - } + /** + * @description + * Enable/disable the up/down arrows based on the activeDescendant. + */ + checkUpDownButtons() { + const activeElement = document.getElementById(this.activeDescendant); - if (!activeElement) { - this.upButton.setAttribute('aria-disabled', 'true'); - this.downButton.setAttribute('aria-disabled', 'true'); - return; - } + if (!this.moveUpDownEnabled) { + return; + } - if (this.upButton) { - if (activeElement.previousElementSibling) { - this.upButton.setAttribute('aria-disabled', false); - } else { + if (!activeElement) { this.upButton.setAttribute('aria-disabled', 'true'); + this.downButton.setAttribute('aria-disabled', 'true'); + return; } - } - if (this.downButton) { - if (activeElement.nextElementSibling) { - this.downButton.setAttribute('aria-disabled', false); - } else { - this.downButton.setAttribute('aria-disabled', 'true'); + if (this.upButton) { + if (activeElement.previousElementSibling) { + this.upButton.setAttribute('aria-disabled', false); + } else { + this.upButton.setAttribute('aria-disabled', 'true'); + } } - } -}; -/** - * @description - * Add the specified items to the listbox. Assumes items are valid options. - * @param items - * An array of items to add to the listbox - */ -aria.Listbox.prototype.addItems = function (items) { - if (!items || !items.length) { - return; + if (this.downButton) { + if (activeElement.nextElementSibling) { + this.downButton.setAttribute('aria-disabled', false); + } else { + this.downButton.setAttribute('aria-disabled', 'true'); + } + } } - items.forEach( - function (item) { - this.defocusItem(item); - this.toggleSelectItem(item); - this.listboxNode.append(item); - }.bind(this) - ); + /** + * @description + * Add the specified items to the listbox. Assumes items are valid options. + * @param items + * An array of items to add to the listbox + */ + addItems(items) { + if (!items || !items.length) { + return; + } - if (!this.activeDescendant) { - this.focusItem(items[0]); - } + items.forEach( + function (item) { + this.defocusItem(item); + this.toggleSelectItem(item); + this.listboxNode.append(item); + }.bind(this) + ); - this.handleItemChange('added', items); -}; + if (!this.activeDescendant) { + this.focusItem(items[0]); + } -/** - * @description - * Remove all of the selected items from the listbox; Removes the focused items - * in a single select listbox and the items with aria-selected in a multi - * select listbox. - * @returns {Array} - * An array of items that were removed from the listbox - */ -aria.Listbox.prototype.deleteItems = function () { - var itemsToDelete; + this.handleItemChange('added', items); + } + + /** + * @description + * Remove all of the selected items from the listbox; Removes the focused items + * in a single select listbox and the items with aria-selected in a multi + * select listbox. + * @returns {Array} + * An array of items that were removed from the listbox + */ + deleteItems() { + let itemsToDelete; + + if (this.multiselectable) { + itemsToDelete = this.listboxNode.querySelectorAll( + '[aria-selected="true"]' + ); + } else if (this.activeDescendant) { + itemsToDelete = [document.getElementById(this.activeDescendant)]; + } - if (this.multiselectable) { - itemsToDelete = this.listboxNode.querySelectorAll('[aria-selected="true"]'); - } else if (this.activeDescendant) { - itemsToDelete = [document.getElementById(this.activeDescendant)]; - } + if (!itemsToDelete || !itemsToDelete.length) { + return []; + } - if (!itemsToDelete || !itemsToDelete.length) { - return []; - } + itemsToDelete.forEach( + function (item) { + item.remove(); - itemsToDelete.forEach( - function (item) { - item.remove(); + if (item.id === this.activeDescendant) { + this.clearActiveDescendant(); + } + }.bind(this) + ); - if (item.id === this.activeDescendant) { - this.clearActiveDescendant(); - } - }.bind(this) - ); + this.handleItemChange('removed', itemsToDelete); - this.handleItemChange('removed', itemsToDelete); + return itemsToDelete; + } - return itemsToDelete; -}; + clearActiveDescendant() { + this.activeDescendant = null; + this.listboxNode.setAttribute('aria-activedescendant', null); -aria.Listbox.prototype.clearActiveDescendant = function () { - this.activeDescendant = null; - this.listboxNode.setAttribute('aria-activedescendant', null); + this.updateMoveButton(); + this.checkUpDownButtons(); + } - this.updateMoveButton(); - this.checkUpDownButtons(); -}; + /** + * @description + * Shifts the currently focused item up on the list. No shifting occurs if the + * item is already at the top of the list. + */ + moveUpItems() { + if (!this.activeDescendant) { + return; + } -/** - * @description - * Shifts the currently focused item up on the list. No shifting occurs if the - * item is already at the top of the list. - */ -aria.Listbox.prototype.moveUpItems = function () { - if (!this.activeDescendant) { - return; - } + const currentItem = document.getElementById(this.activeDescendant); + const previousItem = currentItem.previousElementSibling; - var currentItem = document.getElementById(this.activeDescendant); - var previousItem = currentItem.previousElementSibling; + if (previousItem) { + this.listboxNode.insertBefore(currentItem, previousItem); + this.handleItemChange('moved_up', [currentItem]); + } - if (previousItem) { - this.listboxNode.insertBefore(currentItem, previousItem); - this.handleItemChange('moved_up', [currentItem]); + this.checkUpDownButtons(); } - this.checkUpDownButtons(); -}; + /** + * @description + * Shifts the currently focused item down on the list. No shifting occurs if + * the item is already at the end of the list. + */ + moveDownItems() { + if (!this.activeDescendant) { + return; + } -/** - * @description - * Shifts the currently focused item down on the list. No shifting occurs if - * the item is already at the end of the list. - */ -aria.Listbox.prototype.moveDownItems = function () { - if (!this.activeDescendant) { - return; - } + var currentItem = document.getElementById(this.activeDescendant); + var nextItem = currentItem.nextElementSibling; - var currentItem = document.getElementById(this.activeDescendant); - var nextItem = currentItem.nextElementSibling; + if (nextItem) { + this.listboxNode.insertBefore(nextItem, currentItem); + this.handleItemChange('moved_down', [currentItem]); + } - if (nextItem) { - this.listboxNode.insertBefore(nextItem, currentItem); - this.handleItemChange('moved_down', [currentItem]); + this.checkUpDownButtons(); } - this.checkUpDownButtons(); -}; + /** + * @description + * Delete the currently selected items and add them to the sibling list. + */ + moveItems() { + if (!this.siblingList) { + return; + } -/** - * @description - * Delete the currently selected items and add them to the sibling list. - */ -aria.Listbox.prototype.moveItems = function () { - if (!this.siblingList) { - return; + var itemsToMove = this.deleteItems(); + this.siblingList.addItems(itemsToMove); } - var itemsToMove = this.deleteItems(); - this.siblingList.addItems(itemsToMove); -}; - -/** - * @description - * Enable Up/Down controls to shift items up and down. - * @param upButton - * Up button to trigger up shift - * @param downButton - * Down button to trigger down shift - */ -aria.Listbox.prototype.enableMoveUpDown = function (upButton, downButton) { - this.moveUpDownEnabled = true; - this.upButton = upButton; - this.downButton = downButton; - upButton.addEventListener('click', this.moveUpItems.bind(this)); - downButton.addEventListener('click', this.moveDownItems.bind(this)); -}; + /** + * @description + * Enable Up/Down controls to shift items up and down. + * @param upButton + * Up button to trigger up shift + * @param downButton + * Down button to trigger down shift + */ + enableMoveUpDown(upButton, downButton) { + this.moveUpDownEnabled = true; + this.upButton = upButton; + this.downButton = downButton; + upButton.addEventListener('click', this.moveUpItems.bind(this)); + downButton.addEventListener('click', this.moveDownItems.bind(this)); + } -/** - * @description - * Enable Move controls. Moving removes selected items from the current - * list and adds them to the sibling list. - * @param button - * Move button to trigger delete - * @param siblingList - * Listbox to move items to - */ -aria.Listbox.prototype.setupMove = function (button, siblingList) { - this.siblingList = siblingList; - this.moveButton = button; - button.addEventListener('click', this.moveItems.bind(this)); -}; + /** + * @description + * Enable Move controls. Moving removes selected items from the current + * list and adds them to the sibling list. + * @param button + * Move button to trigger delete + * @param siblingList + * Listbox to move items to + */ + setupMove(button, siblingList) { + this.siblingList = siblingList; + this.moveButton = button; + button.addEventListener('click', this.moveItems.bind(this)); + } -aria.Listbox.prototype.setHandleItemChange = function (handlerFn) { - this.handleItemChange = handlerFn; -}; + setHandleItemChange(handlerFn) { + this.handleItemChange = handlerFn; + } -aria.Listbox.prototype.setHandleFocusChange = function (focusChangeHandler) { - this.handleFocusChange = focusChangeHandler; + setHandleFocusChange(focusChangeHandler) { + this.handleFocusChange = focusChangeHandler; + } }; diff --git a/content/patterns/listbox/examples/js/toolbar.js b/content/patterns/listbox/examples/js/toolbar.js index 6def976c..a1fb9f89 100644 --- a/content/patterns/listbox/examples/js/toolbar.js +++ b/content/patterns/listbox/examples/js/toolbar.js @@ -7,6 +7,9 @@ /** * @namespace aria + * @description + * The aria namespace is used to support sharing class definitions between example files + * without causing eslint errors for undefined classes */ var aria = aria || {}; @@ -17,101 +20,118 @@ var aria = aria || {}; * @param toolbarNode * The DOM node pointing to the toolbar */ -aria.Toolbar = function (toolbarNode) { - this.toolbarNode = toolbarNode; - this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); - this.selectedItem = this.toolbarNode.querySelector('.selected'); - this.registerEvents(); -}; -/** - * @description - * Register events for the toolbar interactions - */ -aria.Toolbar.prototype.registerEvents = function () { - this.toolbarNode.addEventListener( - 'keydown', - this.checkFocusChange.bind(this) - ); - this.toolbarNode.addEventListener('click', this.checkClickItem.bind(this)); -}; +aria.Toolbar = class Toolbar { + constructor(toolbarNode) { + this.toolbarNode = toolbarNode; + this.items = this.toolbarNode.querySelectorAll('.toolbar-item'); + this.selectedItem = this.toolbarNode.querySelector('.selected'); + this.registerEvents(); + } -/** - * @description - * Handle various keyboard controls; LEFT/RIGHT will shift focus; DOWN - * activates a menu button if it is the focused item. - * @param evt - * The keydown event object - */ -aria.Toolbar.prototype.checkFocusChange = function (evt) { - var key = evt.which || evt.keyCode; - var nextIndex, nextItem; + /** + * @description + * Register events for the toolbar interactions + */ + registerEvents() { + this.toolbarNode.addEventListener( + 'keydown', + this.checkFocusChange.bind(this) + ); + this.toolbarNode.addEventListener('click', this.checkClickItem.bind(this)); + } + + /** + * @description + * Handle various keyboard commands to move focus: + * LEFT: Previous button + * RIGHT: Next button + * HOME: First button + * END: Last button + * @param evt + * The keydown event object + */ + checkFocusChange(evt) { + let nextIndex, nextItem; + + // Do not move focus if any modifier keys pressed + if (!evt.shiftKey && !evt.metaKey && !evt.altKey && !evt.ctrlKey) { + switch (evt.key) { + case 'ArrowLeft': + case 'ArrowRight': + nextIndex = Array.prototype.indexOf.call( + this.items, + this.selectedItem + ); + nextIndex = evt.key === 'ArrowLeft' ? nextIndex - 1 : nextIndex + 1; + nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); + + nextItem = this.items[nextIndex]; + break; - switch (key) { - case aria.KeyCode.LEFT: - case aria.KeyCode.RIGHT: - nextIndex = Array.prototype.indexOf.call(this.items, this.selectedItem); - nextIndex = key === aria.KeyCode.LEFT ? nextIndex - 1 : nextIndex + 1; - nextIndex = Math.max(Math.min(nextIndex, this.items.length - 1), 0); + case 'End': + nextItem = this.items[this.items.length - 1]; + break; - nextItem = this.items[nextIndex]; - this.selectItem(nextItem); - this.focusItem(nextItem); - break; - case aria.KeyCode.DOWN: - // if selected item is menu button, pressing DOWN should act like a click - if (aria.Utils.hasClass(this.selectedItem, 'menu-button')) { + case 'Home': + nextItem = this.items[0]; + break; + } + + if (nextItem) { + this.selectItem(nextItem); + this.focusItem(nextItem); + evt.stopPropagation(); evt.preventDefault(); - this.selectedItem.click(); } - break; + } } -}; -/** - * @description - * Selects a toolbar item if it is clicked - * @param evt - * The click event object - */ -aria.Toolbar.prototype.checkClickItem = function (evt) { - if (aria.Utils.hasClass(evt.target, 'toolbar-item')) { - this.selectItem(evt.target); + /** + * @description + * Selects a toolbar item if it is clicked + * @param evt + * The click event object + */ + checkClickItem(evt) { + if (evt.target.classList.contains('toolbar-item')) { + this.selectItem(evt.target); + } } -}; -/** - * @description - * Deselect the specified item - * @param element - * The item to deselect - */ -aria.Toolbar.prototype.deselectItem = function (element) { - aria.Utils.removeClass(element, 'selected'); - element.setAttribute('aria-selected', 'false'); - element.setAttribute('tabindex', '-1'); -}; + /** + * @description + * Deselect the specified item + * @param element + * The item to deselect + */ + deselectItem(element) { + element.classList.remove('selected'); + element.setAttribute('aria-selected', 'false'); + element.setAttribute('tabindex', '-1'); + } -/** - * @description - * Deselect the currently selected item and select the specified item - * @param element - * The item to select - */ -aria.Toolbar.prototype.selectItem = function (element) { - this.deselectItem(this.selectedItem); - aria.Utils.addClass(element, 'selected'); - element.setAttribute('aria-selected', 'true'); - element.setAttribute('tabindex', '0'); - this.selectedItem = element; -}; + /** + * @description + * Deselect the currently selected item and select the specified item + * @param element + * The item to select + */ + selectItem(element) { + this.deselectItem(this.selectedItem); + element.classList.add('selected'); + element.setAttribute('aria-selected', 'true'); + element.setAttribute('tabindex', '0'); + this.selectedItem = element; + } -/** - * @description - * Focus on the specified item - * @param element - * The item to focus on - */ -aria.Toolbar.prototype.focusItem = function (element) { - element.focus(); + /** + * @description + * Focus on the specified item + * @param element + * The item to focus on + */ + focusItem(element) { + element.focus(); + } }; diff --git a/content/patterns/listbox/examples/listbox-collapsible.html b/content/patterns/listbox/examples/listbox-collapsible.html index 2c08fc81..bc27a272 100644 --- a/content/patterns/listbox/examples/listbox-collapsible.html +++ b/content/patterns/listbox/examples/listbox-collapsible.html @@ -15,7 +15,6 @@ - @@ -39,7 +38,7 @@

Deprecation Warning

- The following example implementation of the Listbox Pattern demonstrates a collapsible single-select listbox widget that is functionally similar to an HTML select input with the attribute size="1". + The following example implementation of the Listbox Pattern demonstrates a collapsible single-select listbox widget that is functionally similar to an HTML select input with the attribute size="1". The widget consists of a button that triggers the display of a listbox. In its default state, the widget is collapsed (the listbox is not visible) and the button label shows the currently selected option from the listbox. When the button is activated, the listbox is displayed and the current option is focused and selected. @@ -98,24 +97,23 @@

Example

-

Notes

-

This listbox is scrollable; it has more options than its height can accommodate.

-
    + + +
    +

    Accessibility Features

    +
+
@@ -124,6 +122,12 @@

Keyboard Support

The example listbox on this page implements the following keyboard interface. Other variations and options for the keyboard interface are described in the Keyboard Interaction section of the Listbox Pattern.

+

+ NOTE: When visual focus is on an option in this listbox implementation, DOM focus remains on the listbox element and the value of aria-activedescendant on the listbox refers to the descendant option that is visually indicated as focused. + Where the following descriptions of keyboard commands mention focus, they are referring to the visual focus indicator, not DOM focus. + For more information about this focus management technique, see + Managing Focus in Composites Using aria-activedescendant. +

@@ -132,6 +136,15 @@

Keyboard Support

+ + + + - + - + - + - + - + - + - +
Tab +
    +
  • Moves focus into and out of the listbox.
  • +
  • If the listbox is expanded, selects the focused option, collapses the listbox, and moves focus out of the listbox.
  • +
+
Enter @@ -202,7 +215,7 @@

Role, Property, State, and Tabindex Attributes

aria-labelledby="ID_REF1 ID_REF2"aria-labelledby="ID_REF1 ID_REF2" button
    @@ -214,13 +227,13 @@

    Role, Property, State, and Tabindex Attributes

aria-haspopup="listbox"aria-haspopup="listbox" button Indicates that activating the button displays a listbox.
aria-expanded="true"aria-expanded="true" button
    @@ -237,13 +250,13 @@

    Role, Property, State, and Tabindex Attributes

aria-labelledby="ID_REF"aria-labelledby="ID_REF" ul Refers to the element containing the listbox label.
tabindex="-1"tabindex="-1" ul
    @@ -254,14 +267,13 @@

    Role, Property, State, and Tabindex Attributes

aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
    -
  • Set by the JavaScript when it displays and sets focus on the listbox; otherwise is not present.
  • -
  • Refers to the option in the listbox that is visually indicated as having keyboard focus.
  • +
  • When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
  • +
  • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the listbox element.
  • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
  • -
  • Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the ul element.
  • For more information about this focus management technique, see Managing Focus in Composites Using aria-activedescendant. @@ -277,7 +289,7 @@

    Role, Property, State, and Tabindex Attributes

aria-selected="true"aria-selected="true" li diff --git a/content/patterns/listbox/examples/listbox-grouped.html b/content/patterns/listbox/examples/listbox-grouped.html index b3525cc6..c838e4a7 100644 --- a/content/patterns/listbox/examples/listbox-grouped.html +++ b/content/patterns/listbox/examples/listbox-grouped.html @@ -1,9 +1,9 @@ - + - Listbox Example with Grouped Options + グループ化されたオプションを持つ Listbox の例 @@ -15,7 +15,6 @@ - @@ -27,127 +26,179 @@
-

Listbox Example with Grouped Options

+

グループ化されたオプションを持つ Listbox の例

+
-

About This Example

+

この例について

- The following example implementation of the Listbox Pattern demonstrates a single-select listbox widget with grouped options. - This widget is functionally similar to an HTML select element with size greater than 1 and options grouped into categories with labeled optgroup elements. + 次の例の実装は、グループ化されたオプションを持つ単一選択リストボックスウィジェットのリストボックスパターンを示しています。 + このウィジェットは、sizeが1より大きく、optgroup要素でラベル付けされたカテゴリ内にオプションがグループ化されているHTML select要素と機能的に似ています。

-

Similar examples include:

+

類似の例には以下があります:

-

Example

+

- +
- Choose your animal sidekick + あなたの動物の相棒を選んでください
    - -
  • Cat
  • -
  • Dog
  • -
  • Tiger
  • -
  • Reindeer
  • -
  • Raccoon
  • + +
  • + + 猫 +
  • +
  • + + 犬 +
  • +
  • + + 虎 +
  • +
  • + + トナカイ +
  • +
  • + + アライグマ +
    - -
  • Dolphin
  • -
  • Flounder
  • -
  • Eel
  • + +
  • + + イルカ +
  • +
  • + + ヒラメ +
  • +
  • + + ウナギ +
    - -
  • Falcon
  • -
  • Winged Horse
  • -
  • Owl
  • + +
  • + + ハヤブサ +
  • +
  • + + 翼のある馬 +
  • +
  • + + フクロウ +
- -

Notes

-

This listbox is scrollable; it has more options than its height can accommodate.

-
    -
  1. - Scrolling only works as expected if the listbox is the options' offsetParent. - The example uses position: relative on the listbox to that effect. -
  2. + +
+ + +
+

アクセシビリティ機能

+
  • - When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
      -
    1. If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
    2. -
    3. If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
    4. -
    5. If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
    6. -
    7. If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
    8. -
    + このリストボックスの実装はスクロール可能であり、aria-activedescendant を使用してどのオプションがフォーカスされているかを管理しているため、JavaScript はフォーカスされているオプションが表示されていることを確認しなければならない。 + したがって、キーボード又はポインタのイベントが aria-activedescendant で参照されるオプションを変更する場合、参照されるオプションが完全に表示されていない場合、JavaScript はリストボックスをスクロールしてオプションを表示位置にします。
  • -
  • When a fully visible option is focused in any way, no scrolling occurs.
  • - Normal scrolling through any scrolling mechanism (including Page Up and Page Down) works as expected. - The scroll position will jump as described for focusItem if a means other than a mouse click is used to change focus after scrolling. + リストボックスの操作時の知覚を高めるために、視覚的なキーボードのフォーカスとホバーは CSS の :hover 及び :focus 擬似クラスを使用してスタイルが適用されています: +
      +
    • 視覚的な障害を持つ利用者がリストボックスをインタラクティブな要素として特定するのを助けるため、リストの上にホバーするとカーソルがポインタに変わります。
    • +
    • 選択されたリストボックスのオプションを他のオプションと区別しやすくするため、選択するとオプションの上下に 2 ピクセルの境界線が作成されます。
    • +
  • - +
-

Keyboard Support

+

キーボードサポート

- The example listboxes on this page implement the following keyboard interface. - Other variations and options for the keyboard interface are described in the Keyboard Interaction section of the Listbox Pattern. + このページの例のリストボックスは、以下のキーボードインターフェースを実装しています。 + キーボードインターフェースの他のバリエーション及びオプションについては、リストボックスのパターンのキーボードインタラクションセクションで説明されています。 +

+

+ 注記: このリストボックスの実装において視覚的なフォーカスがオプションにある場合、DOM のフォーカスはリストボックス要素に留まり、リストボックス上の aria-activedescendant の値は視覚的にフォーカスが示されている子孫オプションを参照しています。 + 以下のキーボードコマンドの説明でフォーカスに言及している場合、それは視覚的なフォーカスインジケータを指しており、DOM のフォーカスを指していません。 + このフォーカス管理テクニックに関する詳しい情報については、 + aria-activedescendant を使用したコンポジットのフォーカス管理を参照してください。

- - + + + + + + - - + + - - + + - + - + + + + +
KeyFunctionキー機能
Tabリストボックス内及び外へのフォーカスを移動する。
Down ArrowMoves focus to and selects the next option.次のオプションへのフォーカスを移動し、選択する。
Up ArrowMoves focus to and selects the previous option.前のオプションへのフォーカスを移動し、選択する。
HomeMoves focus to and selects the first option.最初のオプションへのフォーカスを移動し、選択する。
EndMoves focus to and selects the last option.最後のオプションへのフォーカスを移動し、選択する。
印刷可能な文字 +
    +
  • 文字を入力: フォーカスは入力された文字で始まる名前の次の項目へ移動する。
  • +
  • 短い時間内に複数の文字を入力: フォーカスは入力された文字列で始まる名前の次の項目へ移動する。
  • +
+
-

Role, Property, State, and Tabindex Attributes

+

ロール、プロパティ、ステート、及び tabindex 属性

- The example listboxes on this page implement the following ARIA roles, states, and properties. - Information about other ways of applying ARIA roles, states, and properties is available in the Roles, States, and Properties section of the Listbox Pattern. + このページの例示されたリストボックスは、次のARIAのロール、ステート、及びプロパティを実装しています。 + ARIAのロール、ステート、及びプロパティの他の適用方法については、リストボックスパターンのロール、ステート、及びプロパティのセクションで利用可能です。

- - - - + + + + @@ -155,29 +206,34 @@

Role, Property, State, and Tabindex Attributes

- + - + - + - + - + - + @@ -185,38 +241,46 @@

Role, Property, State, and Tabindex Attributes

- + - + - + - + - + + + + + + +
RoleAttributeElementUsageロール属性要素使用法
listbox divIdentifies the focusable element that has listbox behaviors and contains the listbox options.リストボックスの動作を持ち、リストボックスのオプションを含むフォーカス可能な要素を特定する。
aria-labelledby="ID_REF"aria-labelledby="ID_REF" divRefers to the element containing the listbox label.リストボックスのラベルを含む要素を参照する。
tabindex="0"tabindex="0" divIncludes the listbox in the page tab sequence.ページのタブ順序にリストボックスを含める。
aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" div
    -
  • Tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
  • -
  • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
  • -
  • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
  • +
  • リストボックス内のオプションがキーボードフォーカスを持つと視覚的に示されている場合、そのオプションを指す。
  • +
  • DOMのフォーカスがリストボックス要素に留まる間、アプリケーションがフォーカスしているとみなす要素を支援技術が知ることを可能にする。
  • +
  • 例えば下矢印のようなナビゲーションキーが押されると、JavaScriptが値を変更する。
  • +
  • + このフォーカス管理テクニックに関する詳細は、 + aria-activedescendantを使用したコンポジットのフォーカス管理 + を参照してください。 +
group ulIdentifies a group of related options関連するオプションのグループを特定する
aria-labelledby="ID_REF"aria-labelledby="ID_REF" ulRefers to the element containing the option group label.オプショングループのラベルを含む要素を指す。
option liIdentifies each selectable element containing the name of an option.オプションの名前を含む選択可能な要素を特定する。
aria-selected="true"aria-selected="true" li
    -
  • Indicates that the option is selected.
  • -
  • Applied to the element with role option that is visually styled as selected.
  • -
  • The option with this attribute is always the same as the option that is referenced by aria-activedescendant because it is a single-select listbox where selection follows focus.
  • +
  • オプションが選択されていることを示す。
  • +
  • 選択として視覚的にスタイルが適用されたロール option の要素に適用される。
  • +
  • この属性を持つオプションは、選択がフォーカスに従う単一選択リストボックスであるため、常に aria-activedescendant で参照されるオプションと同じである。
aria-hidden="true"span + チェックマークのアイコン用に使用されるキャラクターエンティティをアクセシビリティツリーから削除し、オプションのアクセシブルな名前に含まれるのを防ぐ。 +
-

JavaScript and CSS Source Code

+

JavaScript 及び CSS ソースコード

-

HTML Source Code

- +

HTML ソースコード

+
- + @@ -28,215 +27,313 @@
-

Example Listboxes with Rearrangeable Options

+

並べ替え可能なオプションを持つ例のListboxes

+
-

About This Example

+

この例について

- The following two example implementations of the Listbox Pattern demonstrate differences between single-select and multi-select functionality. - In both examples, users can use action buttons to move options from one list to another. - In the first implementation, users can choose a single option and then activate an action button while in the second example, they may select multiple options before activating an action button. + 次の2つの例の実装は、リストボックスのパターンにおける単一選択と複数選択の機能の違いを示しています。 + 両方の例で、利用者はアクションボタンを使用して、一つのリストから別のリストにオプションを移動することができます。 + 最初の実装では、利用者は単一のオプションを選択し、アクションボタンを作動することができますが、2つ目の例では、アクションボタンを作動する前に複数のオプションを選択することができます。

-

Similar examples include:

+

似たような例には以下があります:

-

Examples

+

-

Example 1: Single-Select Listbox

+

例 1: シングルセレクト リストボックス

- +

- Rank features important to you when choosing where to live. - If a feature is unimportant, move it to the unimportant features list. + 住む場所を選ぶ際に重要と思う特徴をランク付けしてください。 + 特徴が重要でない場合は、重要でない特徴のリストに移動してください。

- Important Features: -
    -
  • Proximity of public K-12 schools
  • -
  • Proximity of child-friendly parks
  • -
  • Proximity of grocery shopping
  • -
  • Proximity of fast food
  • -
  • Proximity of fine dining
  • -
  • Neighborhood walkability
  • -
  • Availability of public transit
  • -
  • Proximity of hospital and medical services
  • -
  • Level of traffic noise
  • -
  • Access to major highways
  • + 重要な特徴: +
      +
    • + + 公立のK-12学校の近さ +
    • +
    • + + 子供に優しい公園の近さ +
    • +
    • + + グロサリーショッピングの近さ +
    • +
    • + + ファストフードの近さ +
    • +
    • + + ファインダイニングの近さ +
    • +
    • + + 近所の歩行可能性 +
    • +
    • + + 公共交通機関の利用可能性 +
    • +
    • + + 病院及び医療サービスの近さ +
    • +
    • + + 交通の騒音レベル +
    • +
    • + + 主要な高速道路へのアクセス +
    -
    - Unimportant Features: -
      - + 重要でない機能: +
        +
        -
        Last change:
        +
        最後の変更:
      - -

      Notes

      -
        -
      1. - Assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
          -
        1. DOM focus remains on the listbox element.
        2. -
        3. When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
        4. -
        5. If the listbox element does not contain any options, aria-activedescendant does not have a value.
        6. -
        -
      2. -
      3. - When Tab moves focus into either listbox: -
          -
        1. If none of the options are selected, the first option receives focus.
        2. -
        3. If an option is selected, the selected option receives focus.
        4. -
        -
      4. -
      5. Only one option may be selected at a time (have aria-selected="true").
      6. -
      7. - As the user moves focus in the list, selection also moves. - That is, both the value of aria-activedescendant and the element that has aria-selected="true" change. -
      8. -
      +
      -

      Example 2: Multi-Select Listbox

      +

      例 2: 複数選択リストボックス

      - +
      -

      Choose upgrades for your transport capsule.

      +

      あなたのトランスポートカプセルのアップグレードを選んでください。

      - Available upgrades: -
        -
      • Leather seats
      • -
      • Front seat warmers
      • -
      • Rear bucket seats
      • -
      • Rear seat warmers
      • -
      • Front sun roof
      • -
      • Rear sun roof
      • -
      • Cloaking capability
      • -
      • Food synthesizer
      • -
      • Advanced waste recycling system
      • -
      • Turbo vertical take-off capability
      • + 利用可能なアップグレード: +
          +
        • + + レザーシート +
        • +
        • + + フロントシートヒーター +
        • +
        • + + リアバケットシート +
        • +
        • + + リアシートヒーター +
        • +
        • + + フロントサンルーフ +
        • +
        • + + リア サンルーフ +
        • +
        • + + クローキング 機能 +
        • +
        • + + フード シンセサイザー +
        • +
        • + + アドバンス廃棄物リサイクル システム +
        • +
        • + + ターボ垂直離陸 機能 +
        - +
      - Upgrades you have chosen: -
        - + 選択したアップグレード: +
          +
        +
        -
        Last change:
        +
        最後の変更:
        - -

        Notes

        -
          -
        1. - Like in example 1, assistive technologies are told which option in the list is visually focused by the value of aria-activedescendant: -
            -
          1. DOM focus remains on the listbox element.
          2. -
          3. When a key that moves focus is pressed or an option is clicked, JavaScript changes the value of aria-activedescendant on the listbox element.
          4. -
          5. If the listbox element does not contain any options, aria-activedescendant does not have a value.
          6. -
          -
        2. -
        3. - When Tab moves focus into either listbox: -
            -
          1. If none of the options are selected, focus is set on the first option.
          2. -
          3. If one or more options are selected, focus is set on the first selected option.
          4. -
          -
        4. -
        5. - Unlike example 1, more than one option may be selected at a time (have aria-selected="true"). -
            -
          1. The multi-select capability is communicated to assistive technologies by setting aria-multiselectable="true" on the listbox element.
          2. -
          3. All option elements have a value set for aria-selected.
          4. -
          5. Selected options have aria-selected set to true and all others have it set to false.
          6. -
          7. Keys that move focus do not change the selected state of an option.
          8. -
          -
        6. -
        7. Users can toggle the selected state of the focused option with Space or click.
        8. -
        +
        -

        Accessibility Features

        -
          +

          アクセシビリティ機能

          +
          • - Keyboard shortcuts for action buttons: -
              + アクションボタンのキーボードショートカット: +
              • - Action buttons have the following shortcuts: + アクションボタンは以下のショートカットを持っています:
                  -
                • "Up": Alt + Up Arrow
                • -
                • "Down": Alt + Down Arrow
                • -
                • "Add": Enter
                • -
                • "Not Important", "Important", and "Remove": Delete
                • +
                • "上": Alt + 上矢印
                • +
                • "下": Alt + 下矢印
                • +
                • "追加": Enter
                • +
                • "重要ではない"、"重要" 及び "削除": Delete
              • -
              • Availability of the shortcuts is communicated to assistive technologies via the aria-keyshortcuts property on the button elements.
              • +
              • ショートカットの利用可能性は、ボタン要素の aria-keyshortcuts プロパティを通じて支援技術に伝えられます。
              • - Each shortcut is only captured when focus is in a context where it is relevant. - For example, Enter performs an add only when focus is in the available options list, and Delete performs a remove only when focus is in the chosen options list. + 各ショートカットは、関連するコンテキストでフォーカスがある場合のみキャプチャされます。 + 例えば、利用可能なオプションのリストでフォーカスがある場合、Enter は追加を実行し、選択したオプションのリストでフォーカスがある場合、Delete は削除を実行します。
              • - Using a shortcut key intentionally places focus to optimize both screen reader and keyboard usability. - For example, pressing Alt + Up Arrow in the "Important Features" list keeps focus on the option that is moved up, enabling all keyboard users to easily perform consecutive move operations for an option and screen reader users to hear the position of an option after it is moved. - Similarly, pressing Enter in the available options list leaves focus in the available options list. - If the option that had focus before the add operation is no longer present in the list, focus lands on the first of the subsequent options that is still present. + ショートカットキーを意図的に使用すると、スクリーンリーダー及びキーボードの利用性を最適化するためにフォーカスが配置されます。 + 例えば、"重要な機能"のリストで Alt + 上矢印 を押すと、移動されるオプションにフォーカスが維持され、すべてのキーボード利用者がオプションの連続した移動操作を簡単に実行でき、スクリーンリーダー利用者が移動後のオプションの位置を聞くことができます。 + 同様に、利用可能なオプションのリストで Enter を押すと、利用可能なオプションのリストにフォーカスが残ります。 + 追加操作前のフォーカスがあったオプションがリストに存在しない場合、フォーカスはまだ存在する次のオプションの最初に着地します。
              • -
            +
          + +
        1. 例 1 では、アクションボタンが 4つあるため、ツールバーウィジェットが使用され、すべてのアクションボタンを1つのタブストップにグループ化します。
        2. +
        3. ライブ領域は、完了したアクションの確認を提供します。
        4. +
        5. + このリストボックスの実装はスクロール可能であり、aria-activedescendantを使用してどのオプションにフォーカスがあるかを管理しているため、JavaScriptはフォーカスされているオプションが表示されることを確認する必要があります。 + したがって、キーボード又はポインタイベントが aria-activedescendant によって参照されるオプションを変更する場合、参照されるオプションが完全に表示されていない場合、JavaScriptはリストボックスをスクロールしてオプションを表示位置に配置します。
        6. -
        7. In example 1, since there are four action buttons, a toolbar widget is used to group all the action buttons into a single tab stop.
        8. -
        9. Live regions provide confirmation of completed actions.
        10. -
        +
      • + リストボックスを操作する際の知覚性を向上させるため、視覚的なキーボードフォーカス及びホバーはCSSの :hover 及び :focus 疑似クラスを使用してスタイル付けされています: +
          +
        • 視覚障害のある人々がリストボックスをインタラクティブな要素として特定するのを助けるため、リストにホバーするとカーソルがポインタに変わります。
        • +
        • 選択されたリストボックスのオプションを他のオプションと区別しやすくするため、選択はオプションの上及び下に 2 ピクセルのボーダーを作成します。
        • +
        +
      • +
        -

        Keyboard Support

        +

        キーボードサポート

        - The example listboxes on this page implement the following keyboard interface. - Other variations and options for the keyboard interface are described in the Keyboard Interaction section of the Listbox Pattern. + このページの例のリストボックスは、以下のキーボードインターフェースを実装しています。 + キーボードインターフェースのその他のバリエーション及びオプションは、リストボックスパターンのキーボードインタラクションセクションで説明されています。 +

        +

        + 注記: これらのリストボックスの実装において視覚的なフォーカスがオプションにある場合、DOMフォーカスはリストボックス要素に留まり、リストボックスのaria-activedescendantの値は視覚的にフォーカスされていると指示されている子孫オプションを指します。 + キーボードコマンドの以下の説明でフォーカスについて言及している場合、DOMフォーカスではなく視覚的なフォーカスインジケータを指しています。 + このフォーカス管理テクニックに関する詳細は、 + aria-activedescendantを使用したコンポジットのフォーカス管理を参照してください。

        - - + + + + + + @@ -244,8 +341,8 @@

        Keyboard Support

        @@ -253,56 +350,64 @@

        Keyboard Support

        + + + +
        KeyFunctionキー機能
        Tab +
          +
        • リストボックス内へ及びリストボックスからのフォーカスを移動します。
        • +
        • + リストボックスがフォーカスを受け取ると、オプションが選択されていない場合は、最初のオプションがフォーカスを受け取ります。 + それ以外の場合は、最初に選択されたオプションがフォーカスを受け取ります。 +
        • +
        +
        Down Arrow -
          -
        • Moves focus to the next option.
        • -
        • In the example 1 single-select listboxes, also selects the focused option.
        • -
        +
          +
        • 次のオプションにフォーカスを移動する。
        • +
        • 例の 1 つの単一選択リストボックスでは、フォーカスされているオプションも選択する。
        • +
        Up Arrow
          -
        • Moves focus to the previous option.
        • -
        • In the example 1 single-select listboxes, also selects the focused option.
        • +
        • 前のオプションにフォーカスを移動する。
        • +
        • 例の 1 つの単一選択リストボックスでは、フォーカスされているオプションも選択する。
        Home
          -
        • Moves focus to the first option.
        • -
        • In the example 1 single-select listboxes, also selects the focused option.
        • +
        • 最初のオプションにフォーカスを移動する。
        • +
        • 例の 1 つの単一選択リストボックスでは、フォーカスされているオプションも選択する。
        End
          -
        • Moves focus to the last option.
        • -
        • In the example 1 single-select listboxes, also selects the focused option.
        • +
        • 最後のオプションにフォーカスを移動する。
        • +
        • 例の 1 つの単一選択リストボックスでは、フォーカスされているオプションも選択する。
        • +
        +
        印刷可能な文字 +
          +
        • 文字を入力する: 入力した文字で始まる名前の次の項目にフォーカスが移動する。
        • +
        • 短時間のうちに複数の文字を入力する: 入力された文字列で始まる名前の次の項目にフォーカスが移動する。
        -

        Multiple selection keys supported in example 2

        -
        -

        Note

        -

        The selection behavior demonstrated differs from the behavior provided by browsers for native HTML <select multiple> elements. - The HTML select element behavior is to alter selection with unmodified up/down arrow keys, requiring the use of modifier keys to select multiple options. - This example demonstrates the multiple selection interaction model recommended in the Keyboard Interaction section of the Listbox Pattern, which does not require the use of modifier keys.

        -
        +

        例2でサポートされる複数選択キー

        +

        + 注記: 示されている選択動作は、ネイティブHTMLの <select multiple> 要素のブラウザによる動作とは異なります。 + HTML select 要素の動作は、変更キーなしの上/下の矢印キーで選択を変更し、複数のオプションを選択するために変更キーの使用が必要です。 + この例は、変更キーの使用を必要としないリストボックスパターンのキーボードインタラクションセクションで推奨される複数選択インタラクションモデルを示しています。 +

        - - + + - - + + - - + + - - + + - + - + @@ -310,18 +415,18 @@

        Note

        -

        Role, Property, State, and Tabindex Attributes

        +

        ロール、プロパティ、ステート及び tabindex 属性

        - The example listboxes on this page implement the following ARIA roles, states, and properties. - Information about other ways of applying ARIA roles, states, and properties is available in the Roles, States, and Properties section of the Listbox Pattern. + このページの例示リストボックスは、次のARIA ロール、ステート、及びプロパティを実装しています。 + ARIA ロール、ステート、及びプロパティの他の適用方法に関する情報は、リストボックスパターンのロール、ステート、及びプロパティセクションで利用可能です。

        KeyFunctionキー機能
        Spacechanges the selection state of the focused option .スペースフォーカスされているオプションの選択ステートを変更します。
        Shift + Down ArrowMoves focus to and selects the next option.Shift + 下矢印フォーカスを移動し、次のオプションを選択します。
        Shift + Up ArrowMoves focus to and selects the previous option.Shift + 上矢印フォーカスを移動し、前のオプションを選択します。
        Control + Shift + HomeSelects from the focused option to the beginning of the list.フォーカスされているオプションからリストの先頭までを選択します。
        Control + Shift + EndSelects from the focused option to the end of the list.フォーカスされているオプションからリストの最後までを選択します。
        - Control + A (All Platforms)
        + Control + A (全プラットフォーム)
        Command-A (macOS)
        - Selects all options in the list. - If all options are selected, unselects all options. + リスト内のすべてのオプションを選択します。 + すべてのオプションが選択されている場合、すべてのオプションの選択を解除します。
        - - - - + + + + @@ -329,42 +434,43 @@

        Role, Property, State, and Tabindex Attributes

        - + - + - + - + - + - + - + @@ -372,39 +478,47 @@

        Role, Property, State, and Tabindex Attributes

        - + - + - + + + + + + +
        RoleAttributeElementUsageロール属性要素使用法
        listbox ulIdentifies the focusable element that has listbox behaviors and contains the listbox options.リストボックスの動作を持ち、リストボックスのオプションを含むフォーカス可能な要素を特定する。
        aria-labelledby="ID_REF"aria-labelledby="ID_REF" ulApplied to the element with the listbox role, it refers to the span containing its label.リストボックス ロールを持つ要素に適用され、そのラベルを含むスパンを参照します。
        tabindex="0"tabindex="0" ulApplied to the element with the listbox role, it puts the listbox in the tab sequence.リストボックス ロールを持つ要素に適用され、タブ シーケンスにリストボックスを置きます。
        aria-multiselectable="true"aria-multiselectable="true" ul
          -
        • Used only in example 2.
        • -
        • Applied to the element with the listbox role, tells assistive technologies that the list supports multiple selection.
        • -
        • The default value is false so it is not specified in example 1.
        • +
        • 例 2 でのみ使用。
        • +
        • リストボックス ロールを持つ要素に適用され、リストが複数選択をサポートしていることを支援技術に伝える。
        • +
        • デフォルトの値は false なので、例 1 では指定されていません。
        aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
          -
        • Applied to the element with the listbox role, tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
        • -
        • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
        • -
        • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
        • -
        • When the listbox is empty, aria-activedescendant="".
        • +
        • リストボックス内のオプションがキーボードフォーカスを持っていると視覚的に示される場合、そのオプションを参照します。
        • +
        • DOMのフォーカスがリストボックス要素に残っている間、アプリケーションがフォーカスしていると見なす要素を支援技術が知ることを可能にします。
        • +
        • 下矢印のようなナビゲーションキーが押されると、JavaScriptが値を変更します。
        • +
        • リストボックスが空の場合、aria-activedescendant=""です。
        • +
        • このフォーカス管理テクニックに関する詳細は、aria-activedescendantを使用したコンポジットのフォーカス管理を参照してください。
        option liIdentifies each selectable element containing the name of an option.オプションの名前を含む選択可能な要素を特定する。
        aria-selected="true"aria-selected="true" li
          -
        • Applied to elements with role option that are visually styled as selected to inform assistive technologies that the options are selected.
        • -
        • In example 1, the option with this attribute is always the same as the option that is referenced by aria-activedescendant because it is a single-select listbox where selection follows focus.
        • -
        • In example 2, more than one option may be selected so the user can move focus among options without effecting which options have this attribute.
        • -
        • Note that in example 2, the focus style and selection styles are distinctly different and independent.
        • +
        • 視覚的に選択されたとしてスタイルが適用されたロールオプションの要素に適用され、選択されたオプションであることを支援技術に通知します。
        • +
        • 例1では、この属性を持つオプションは常にaria-activedescendantによって参照されるオプションと同じです。これは、選択がフォーカスに従う単一選択リストボックスだからです。
        • +
        • 例2では、複数のオプションが選択される場合があるため、利用者は選択したオプションにこの属性があるかどうかに影響を与えずにオプション間でフォーカスを移動できます。
        • +
        • 例2では、フォーカスのスタイル及び選択のスタイルは明確に異なり、独立しています。
        aria-selected="false"aria-selected="false" li
          -
        • Used only in example 2.
        • -
        • Applied to elements with role option that are not visually styled as selected to inform assistive technologies that the options are selectable but not selected.
        • -
        • In example 1, this is unnecessary because the selection moves every time the focus moves.
        • +
        • 例 2 でのみ使用。
        • +
        • 選択として視覚的にスタイル設定されていないロール option の要素に適用し、そのオプションが選択可能であるが選択されていないことを支援技術に通知する。
        • +
        • 例 1 では、フォーカスが移動するたびに選択が移動するため、これは不要である。
        aria-hidden="true"span + チェックマーク、左矢印、及び右矢印のために使用される文字エンティティをアクセシビリティツリーから削除して、それらがオプション又はボタンのアクセシブルな名前に含まれるのを防ぐ。 +
        -

        JavaScript and CSS Source Code

        +

        JavaScript 及び CSS ソースコード

        -

        HTML Source Code

        -

        Example 1: Single-Select Listbox

        - +

        HTML ソースコード

        +

        例 1: シングルセレクト リストボックス

        +
        - + -

        Example 2: Multi-Select Listbox

        - +

        例 2: マルチセレクト リストボックス

        +
        - + - + diff --git a/content/patterns/listbox/examples/listbox-scrollable.html b/content/patterns/listbox/examples/listbox-scrollable.html index 1b54fd87..d6d91238 100644 --- a/content/patterns/listbox/examples/listbox-scrollable.html +++ b/content/patterns/listbox/examples/listbox-scrollable.html @@ -1,9 +1,9 @@ - + - Scrollable Listbox Example + スクロール可能な Listbox の例 @@ -15,7 +15,6 @@ - @@ -27,134 +26,226 @@
        -

        Scrollable Listbox Example

        +

        スクロール可能な Listbox の例

        +
        -

        About This Example

        +

        この例について

        - The following example implementation of the Listbox Pattern demonstrates a scrollable single-select listbox widget. - This widget is functionally similar to an HTML select input where the size attribute has a value greater than one. + 以下の例の実装は、スクロール可能な単一選択のリストボックスウィジェットのListbox パターンを示しています。 + このウィジェットは、HTML の select 入力で size 属性が一より大きい値を持つ場合と機能的に似ています。

        -

        Similar examples include:

        +

        類似の例には以下があります:

        -

        Example

        +

        - +
        -

        Choose your favorite transuranic element (actinide or transactinide).

        +

        お好きな超ウラン元素(アクチニウム系またはトランスアクチニウム系)を選んでください。

        Transuranium elements:
          -
        • Neptunium
        • -
        • Plutonium
        • -
        • Americium
        • -
        • Curium
        • -
        • Berkelium
        • -
        • Californium
        • -
        • Einsteinium
        • -
        • Fermium
        • -
        • Mendelevium
        • -
        • Nobelium
        • -
        • Lawrencium
        • -
        • Rutherfordium
        • -
        • Dubnium
        • -
        • Seaborgium
        • -
        • Bohrium
        • -
        • Hassium
        • -
        • Meitnerium
        • -
        • Darmstadtium
        • -
        • Roentgenium
        • -
        • Copernicium
        • -
        • Nihonium
        • -
        • Flerovium
        • -
        • Moscovium
        • -
        • Livermorium
        • -
        • Tennessine
        • -
        • Oganesson
        • +
        • + + ネプツニウム +
        • +
        • + + プルトニウム +
        • +
        • + + アメリシウム +
        • +
        • + + キュリウム +
        • +
        • + + バークリウム +
        • +
        • + + カリフォルニウム +
        • +
        • + + アインスタイニウム +
        • +
        • + + フェルミウム +
        • +
        • + + メンデレビウム +
        • +
        • + + ノーベリウム +
        • +
        • + + ローレンシウム +
        • +
        • + + ラザホーディウム +
        • +
        • + + ドブニウム +
        • +
        • + + シーボーギウム +
        • +
        • + + ボーリウム +
        • +
        • + + ハッシウム +
        • +
        • + + マイトネリウム +
        • +
        • + + ダームスタット +
        • +
        • + + レントゲニウム +
        • +
        • + + コペルニシウム +
        • +
        • + + ニホニウム +
        • +
        • + + フレロビウム +
        • +
        • + + モスコビウム +
        • +
        • + + リバモリウム +
        • +
        • + + テネシン +
        • +
        • + + オガネッソン +
        - -

        Notes

        -

        This listbox is scrollable; it has more options than its height can accommodate.

        -
          -
        1. - Scrolling only works as expected if the listbox is the options' offsetParent. - The example uses position: relative on the listbox to that effect. -
        2. + +
        + +
        +

        アクセシビリティの特徴

        +
        • - When an option is focused that isn't (fully) visible, the listbox's scroll position is updated: -
            -
          1. If Up Arrow or Down Arrow is pressed, the previous or next option is scrolled into view.
          2. -
          3. If Home or End is pressed, the listbox scrolls all the way to the top or to the bottom.
          4. -
          5. If focusItem is called, the focused option will be scrolled to the top of the view if it was located above it or to the bottom if it was below it.
          6. -
          7. If the mouse is clicked on a partially visible option, it will be scrolled fully into view.
          8. -
          + このリストボックスの実装はスクロール可能であり、どのオプションがフォーカスされているかをaria-activedescendantを使用して管理しているため、JavaScriptはフォーカスされているオプションが可視であることを確保しなければなりません。 + したがって、キーボード又はポインタイベントがaria-activedescendantで参照されるオプションを変更する場合、参照されるオプションが完全に表示されていない場合、JavaScriptはリストボックスをスクロールしてオプションが表示される位置に配置します。
        • -
        • When a fully visible option is focused in any way, no scrolling occurs.
        • - Normal scrolling through any scrolling mechanism (including Page Up and Page Down) works as expected. - The scroll position will jump as described for focusItem if a means other than a mouse click is used to change focus after scrolling. + リストボックスを操作するときの知覚性を高めるために、視覚的なキーボードのフォーカス及びホバーはCSSの:hover 及び :focus 疑似クラスを使用してスタイルされています: +
            +
          • 視覚的な障害を持つ人々がリストボックスを対話的な要素として特定するのを助けるため、リストの上にホバーするとカーソルがポインタに変わります。
          • +
          • 選択されたリストボックスのオプションを他のオプションと区別しやすくするため、選択はオプションの上下に2ピクセルの境界線を作成します。
          • +
        • - +
        -

        Keyboard Support

        +

        キーボードサポート

        +

        + このページの例示されたリストボックスは、以下のキーボードインターフェースを実装しています。 + キーボードインターフェースのその他のバリエーション及びオプションについては、リストボックスパターンのキーボードインタラクションセクションで説明されています。 +

        - The example listboxes on this page implement the following keyboard interface. - Other variations and options for the keyboard interface are described in the Keyboard Interaction section of the Listbox Pattern. + 注記: このリストボックスの実装において視覚的なフォーカスがオプション上にある場合、DOMのフォーカスはリストボックス要素上に残り、リストボックス上のaria-activedescendantの値はフォーカスが指示されていると視覚的に示される子孫オプションを参照します。 + 以下のキーボードコマンドの説明でフォーカスについて言及している場合、それは視覚的なフォーカス指標を指しており、DOMのフォーカスを指していません。 + このフォーカス管理テクニックについての詳細は、aria-activedescendantを使用したコンポジット内のフォーカス管理を参照してください。

        - - + + + + + + - - + + - - + + - + - + + + + +
        KeyFunctionキー機能
        Tabリストボックス内への及びリストボックスからのフォーカスを移動する。
        Down ArrowMoves focus to and selects the next option.下矢印次のオプションへのフォーカスを移動し、選択する。
        Up ArrowMoves focus to and selects the previous option.上矢印前のオプションへのフォーカスを移動し、選択する。
        HomeMoves focus to and selects the first option.最初のオプションへのフォーカスを移動し、選択する。
        EndMoves focus to and selects the last option.最後のオプションへのフォーカスを移動し、選択する。
        印刷可能な文字 +
          +
        • 文字を入力する: フォーカスは入力された文字で始まる名前の次のアイテムに移動する。
        • +
        • 短時間で複数の文字を入力する: フォーカスは入力された文字列で始まる名前の次のアイテムに移動する。
        • +
        +
        -

        Role, Property, State, and Tabindex Attributes

        +

        ロール、プロパティ、ステート、及び tabindex 属性

        - The example listboxes on this page implement the following ARIA roles, states, and properties. - Information about other ways of applying ARIA roles, states, and properties is available in the Roles, States, and Properties section of the Listbox Pattern. + このページの例示リストボックスは、以下の ARIA ロール、ステート、及びプロパティを実装しています。 + ARIA ロール、ステート、及びプロパティの他の適用方法に関する情報は、リストボックスパターンのロール、ステート、及びプロパティセクションで利用可能です。

        - - - - + + + + @@ -162,29 +253,34 @@

        Role, Property, State, and Tabindex Attributes

        - + - + - + - + - + - + @@ -192,26 +288,34 @@

        Role, Property, State, and Tabindex Attributes

        - + - + + + + + + +
        RoleAttributeElementUsageロール属性要素使用法
        listbox ulIdentifies the focusable element that has listbox behaviors and contains the listbox options.リストボックスの動作を持ち、リストボックスのオプションを含むフォーカス可能な要素を識別します。
        aria-labelledby="ID_REF"aria-labelledby="ID_REF" ulRefers to the element containing the listbox label.リストボックスのラベルを含む要素を参照します。
        tabindex="0"tabindex="0" ulIncludes the listbox in the page tab sequence.ページのタブ順序にリストボックスを含めます。
        aria-activedescendant="ID_REF"aria-activedescendant="ID_REF" ul
          -
        • Tells assistive technologies which of the options, if any, is visually indicated as having keyboard focus.
        • -
        • DOM focus remains on the ul element and the idref specified for aria-activedescendant refers to the li element that is visually styled as focused.
        • -
        • When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
        • +
        • リストボックス内のオプションがキーボードフォーカスを持っていると視覚的に示されている場合、そのオプションを参照します。
        • +
        • DOMフォーカスがリストボックス要素に残っている間、アプリケーションがフォーカスしているとみなす要素を支援技術が知ることができます。
        • +
        • ナビゲーションキー、たとえばDown Arrowが押されたとき、JavaScriptが値を変更します。
        • +
        • + このフォーカス管理技術に関する詳しい情報は、 + aria-activedescendantを使用したコンポジット内のフォーカスの管理 + を参照してください。 +
        option liIdentifies each selectable element containing the name of an option.オプションの名前を含む選択可能な要素を特定する。
        aria-selected="true"aria-selected="true" li
          -
        • Indicates that the option is selected.
        • -
        • Applied to the element with role option that is visually styled as selected.
        • -
        • The option with this attribute is always the same as the option that is referenced by aria-activedescendant because it is a single-select listbox where selection follows focus.
        • +
        • オプションが選択されていることを示す。
        • +
        • 選択されていると視覚的にスタイルされたロールオプションの要素に適用される。
        • +
        • この属性を持つオプションは、フォーカスに続く選択がある単一選択リストボックスであるため、常にaria-activedescendantによって参照されるオプションと同じです。
        aria-hidden="true"span + チェックマークアイコンに使用される文字エンティティをアクセシビリティツリーから削除し、オプションのアクセシブルな名前に含められるのを防ぐ。 +
        -

        JavaScript and CSS Source Code

        +

        JavaScript 及び CSS ソースコード

        -

        HTML Source Code

        - +

        HTML ソースコード

        +
        - +