References

Beginner-friendly references for web development, with live, editable examples.

The HTML aria-multiselectable attribute

ARIA Accessibility
In short

Set aria-multiselectable="true" on a custom listbox, grid, tree or tablist when the user can select more than one item. It sits on the container, while each item carries aria-selected. For a form field, <select multiple> gives you this with no ARIA or script.

Overview

The aria-multiselectable property says the user may select more than one of a widget's selectable descendants. A grid, listbox, tablist or tree can take it directly, and a treegrid inherits it. The default, false, allows one item at a time.

A <select> with the multiple attribute is already exposed as aria-multiselectable="true", and ARIA in HTML advises against adding aria-multiselectable to a <select> yourself.

Browsers settle a mismatch differently. Firefox keeps the native state. Chrome reports the attribute on a list box, so <select size="3"> with it set to true is exposed as multi-selectable yet still takes one choice. A drop-down <select> doesn't expose it in either browser.

On a custom widget, the property works with aria-selected. Selected descendants should have aria-selected set to true and selectable ones false, and descendants that can't be selected should not use it at all.

A tab list is the odd case, because several panels can show at once. In a multi-selectable tablist, the tabs should also carry aria-expanded, true when a tab's panel is visible and false when it's hidden.

Syntax

<ul role="listbox" aria-label="Toppings" tabindex="0"
    aria-multiselectable="true" aria-activedescendant="olives">
  <li id="olives" role="option" aria-selected="true">Olives</li>
  <li id="peppers" role="option" aria-selected="false">Peppers</li>
</ul>

Values

Value
true | false

Example

Live example
<p style="margin:0 0 8px">Pick your toppings. Click an option, or focus the list, use the Up and Down arrow keys and press Space.</p>
<ul id="toppings" role="listbox" tabindex="0" aria-label="Pizza toppings" aria-multiselectable="true" aria-activedescendant="olives" style="list-style:none;margin:0 0 8px;padding:4px;max-width:260px;border:1px solid #767676;border-radius:6px">
  <li id="olives" role="option" aria-selected="true" style="padding:2px 6px;background:#d9e8fb;font-weight:bold">Olives</li>
  <li id="peppers" role="option" aria-selected="false" style="padding:2px 6px">Peppers</li>
  <li id="onions" role="option" aria-selected="true" style="padding:2px 6px;background:#d9e8fb;font-weight:bold">Onions</li>
  <li id="basil" role="option" aria-selected="false" style="padding:2px 6px">Basil</li>
</ul>
<p role="status" id="chosen" style="margin:0">2 selected: Olives, Onions.</p>
<script>
const list = document.getElementById('toppings');
const options = [...list.querySelectorAll('[role="option"]')];
let active = 0;
function report() {
  const picked = options.filter(function (o) { return o.getAttribute('aria-selected') === 'true'; });
  document.getElementById('chosen').textContent = picked.length + ' selected' + (picked.length ? ': ' + picked.map(function (o) { return o.textContent; }).join(', ') : '') + '.';
}
function toggle(option) {
  const on = option.getAttribute('aria-selected') !== 'true';
  option.setAttribute('aria-selected', String(on));
  option.style.background = on ? '#d9e8fb' : '';
  option.style.fontWeight = on ? 'bold' : '';
  report();
}
function move(index) {
  active = index;
  list.setAttribute('aria-activedescendant', options[index].id);
  options.forEach(function (o, i) { o.style.outline = i === index ? '2px solid #1e72e0' : ''; });
}
list.addEventListener('focus', function () { move(active); });
list.addEventListener('blur', function () { options[active].style.outline = ''; });
options.forEach(function (option, index) {
  option.addEventListener('click', function () { move(index); toggle(option); });
});
list.addEventListener('keydown', function (event) {
  if (event.key === 'ArrowDown') move((active + 1) % options.length);
  else if (event.key === 'ArrowUp') move((active - 1 + options.length) % options.length);
  else if (event.key === ' ') toggle(options[active]);
  else return;
  event.preventDefault();
});
</script>

Best practices

  • With aria-multiselectable, say how to pick several items, such as by holding the Control key.
  • Writing false on a single-choice widget adds no meaning, because an explicit false and the implied default are the same.
  • Consider a group of checkboxes when a <select multiple> can't be styled the way you need. An <input type="checkbox"> is semantic, focusable and free to style.
  • Manage focus inside a custom listbox. A tabindex on the items or aria-activedescendant on the list can do it.

Frequently asked questions

What does aria-multiselectable=true mean?
Setting aria-multiselectable to true means more than one item in the widget may be selected at a time. With false, the default, only one item can be selected.
Which element carries aria-multiselectable in a listbox?
The listbox itself carries aria-multiselectable, not its options, and a grid, tablist or tree works the same way. Selection is then recorded on each item with aria-selected.
Is the aria-multiselectable JavaScript property spelled with a capital S?
Yes, the property for aria-multiselectable is ariaMultiSelectable, with a capital S. No ariaMultiselectable property is defined, so assigning to that spelling leaves the attribute unset.
How do users pick several items in an aria-multiselectable listbox?
The aria-multiselectable property leaves that to your script. Click and key handlers have to toggle aria-selected on each item.
Why is aria-multiselectable ignored on my list?
The aria-multiselectable property needs a supported role, and a plain ul or a menu doesn't have one. Browsers leave it out of the accessibility tree there.