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
useLabels
extension
- 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
role
aria-orientation
aria-multiselectable
aria-selected
aria-disabled
aria-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 + arrow
transfers focus to the first or last eligible optionmousedown
,touchstart
,Space
andEnter
select the focused option, if it's eligible and not selectedmousedown
,touchstart
,Space
andEnter
deselect the focused option, if it's selectedesc
deselects all options
Shift + arrow
adds the next or previous eligible option to the selectionShift + Command/Control + arrow
adds all preceding or all following eligible options to the selectionCommand/Control + A
selects all eligible options
Example
Single select
0
0
Multiselect
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:
multiselectable
false
initialSelected
0
The 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.
orientation
horizontal
selectsOnFocus
true
loops
true
disabledOptionsReceiveFocus
true
Indicates 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.textContent
A 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.
queryMatchThreshold
1
Configures 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.
needsAriaOwns
false
Indicates 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:
root
root.ref
should be bound to the DOM element that serves as your listbox's root.
options
A 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.
focused
selected
A 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.
is
An 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
.
focus
An 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.
select
An 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: