Listbox
Updated on September 7, 2024Source codeTests
useListbox is a composable that implements the UI logic needed for a reactive, accessible listbox interface.
It follows WAI-ARIA authoring practices and allows you to easily customize accessibility features.
Features
Here's a breakdown of all the features offered by useListbox:
- Supports single, multiple, and no selection
- Compatible with assistive technology
- Typeahead—with optional fuzzy matching—to quickly transfer focus
- Supports enabled/disabled option state, even if that state changes reactively with user interaction
- Support for complex options, with help from the
useLabelsextension
- Focused option and selected option(s) are tracked and controlled reactively
- Via the return object, you can read and watch reactive references to the focused option and selected option(s)
- Via the return object, you can access methods that intelligently mutate the focused option and selected option(s)
- Keyboard interactions adjust their behavior based on reactive changes to options' enabled/disabled state
- Reactively disabled options are automatically deselected
- Focused option and selected option(s) automatically update if option elements are reactively reordered
rolearia-orientationaria-multiselectablearia-selectedaria-disabledaria-owns
- Arrow keys transfer focus to the next or previous eligible option (up and down for vertical listboxes, left and right for horizontal listboxes)
- Keyboard interaction is compatible with listboxes whose options are divided into multiple groups.
Home,End, andCommand/Control + arrowtransfers focus to the first or last eligible optionmousedown,touchstart,SpaceandEnterselect the focused option, if it's eligible and not selectedmousedown,touchstart,SpaceandEnterdeselect the focused option, if it's selectedescdeselects all options
Shift + arrowadds the next or previous eligible option to the selectionShift + Command/Control + arrowadds all preceding or all following eligible options to the selectionCommand/Control + Aselects all eligible options
Example
Single select
00Multiselect
Create a listbox
To create a listbox, call the useListbox function, which accepts one optional options object as its only parameter.
<!-- MyComponent.vue -->
<template>...</template>
<script setup>
import { useListbox } from '@baleada/vue-features'
const listbox = useListbox([options])
</script>
Here's a breakdown of the options object:
multiselectablefalseinitialSelected0The index-based position of the option that should be initially selected.
For multiselectable listboxes, initialSelected can also be an array of index-based positions for multiple selected options.
ability() => 'enabled'A status option that should resolve to enabled for each enabled option, and disabled for each disabled option.
useListbox uses this information to decide which options are eligible to receive focus and/or be selected.
orientationhorizontalselectsOnFocustrueloopstruedisabledOptionsReceiveFocustrueIndicates whether or not your listbox can transfer focus to disabled options.
If you set disabledOptionsReceiveFocus to false, you should be confident that assistive tech users don't need or want to read the labels of disabled options, or that they have another way to access that content.
Note that even when disabledOptionsReceiveFocus is true, it's never possible to select disabled options.
toCandidate(param)({ element }) => element.textContentA callback function that helps your listbox's typeahead feature retrieve searchable text from each option.
When the end user starts typing a query, your listbox will iterate through the listbox options, calling toCandidate for each one to retrieve the "search candidate" text.
toCandidate receives one argument: an object with an element property and an index property. element holds the listbox option's actual DOM element, and index holds the option's index-based position in your array of options.
queryMatchThreshold1Configures fuzzy matching for the typeahead feature.
queryMatchThreshold should be a number from 0 to 1. When queryMatchThreshold is 1, your listbox's typeahead will only transfer focus to an option that is an exact match for the typeahead's query. A numbers less than 1 will be increasingly less strict, ignoring slight typos and query mistakes when it transfers focus.
needsAriaOwnsfalseIndicates whether or not your listbox needs to manage the aria-owns attribute automatically.
needsAriaOwns should only be true if the structure of your HTML markup does not define a clear relationship between the listbox root element and the listbox options.
Use your listbox
useListbox returns a listbox—an object with tools you can use to connect useListbox's UI logic to your markup.
Here's a breakdown of that object:
rootroot.ref should be bound to the DOM element that serves as your listbox's root.
optionsA multiple element API object.
Pass the index-based position (Number) of the current option as the only argument for options.ref, and its returned function ref should be bound to the DOM element that serves as that option.
It's recommended that you render the options with v-for, get the index from your v-for statement, and bind the function ref to the v-for element.
focusedselectedA reactive reference to the index-based position of the currently selected option
For multiselectable listboxes, selected will be an array of numbers, indicating the index-based positions of all selected options, in the order they were selected.
isAn object with two properties: focused and selected.
Each property holds a method that requires an index (Number) as its only parameter.
Given the index, is.focused returns a boolean indicating whether or not that option is focused, and is.selected returns a boolean indicating whether or not that option is selected.
is.focused and is.selected read from reactive references, so their boolean return values are fully reactive when used in Vue templates, watchers, and computed references.
getStatuses(index)A function that requires an index (Number) as its only parameter, and returns a three-item array indicating the statuses (Strings) of the option at that index.
The first item will be focused or blurred, the second item will be selected or deselected and the third item will be enabled or disabled.
focusAn object containing all the methods described in the eligible focus guide.
You can use these methods to programmatically transfer focus in a smarter way, taking enabled/disabled state and other customizable conditions into account.
selectAn object containing all the methods described in the eligible picking guide.
You can use these methods to programmatically select options in a smarter way, taking enabled/disabled state and other customizable conditions into account.
Note that if you have a multiselectable listbox you can optionally pass an array of index-based positions to the select.exact method to programmatically select multiple eligible options.
deselect(indexOrIndices)A function that deselects an option.
For single select listboxes, deselect does not accept any parameters.
For multiselect listboxes, you can either pass a single index-based position (Number) to deselect one option, or pass an array of indices to deselect multiple options, or pass no arguments to deselect all currently selected options.
Here's a more complete example of how to use your listbox and bind the various function refs:
<!-- MyComponent.vue -->
<template>
<div :ref="listbox.root.ref()">
<div
v-for="(option, index) in options"
:key="option"
:ref="listbox.options.ref(index)"
>
{{ option }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useListbox } from '@baleada/vue-features'
const options = ref([
'Educate Girls',
'Kheyti',
'One Heart Worldwide',
])
const listbox = useListbox()
</script>
Extend the listbox
The following extensions are compatible with your textbox: