References

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

The HTML aria-autocomplete attribute

ARIA Accessibility
In short

The aria-autocomplete value tells assistive technology how your code's suggestions appear, as inline, list or both. HTML's autocomplete attribute is a separate feature that handles autofill.

Overview

The aria-autocomplete attribute describes how a combobox, searchbox or textbox presents predictions of what the user is typing. It doesn't provide the functionality, so typing in an input that has it adds no elements and opens no list.

With inline, text completing the input may appear after the caret. With list, a separate element may show possible values. With both, that list can appear with one value selected automatically, and the rest of that value's text after the caret. The default, none, shows no automatic suggestion.

The list and both values come with rules. The element must have aria-controls pointing at the suggestion container and an aria-haspopup value matching the container's role, which a combobox already has for a listbox. Use aria-expanded to show whether the list is displayed.

Inline suggestions have a rule of their own. The suggested part should be presented as selected text, so assistive technology can tell it from what was typed and the user can delete it or replace it by typing on. The suggestion becomes the value if focus leaves the field.

HTML has its own list model. A <datalist> linked from a text-style <input> by its list attribute offers suggestions with no script, and that input has the combobox role. Try it before building a custom widget.

Syntax

<label for="tag">Tag</label>
<input id="tag" role="combobox" aria-autocomplete="list"
       aria-expanded="true" aria-controls="tag-list">
<ul id="tag-list" role="listbox" aria-label="Tags">
  <li role="option" aria-selected="false">Zebra</li>
  <li role="option" aria-selected="false">Zoom</li>
</ul>

Values

Value
none | inline | list | both

Example

Live example
<label for="city">Destination</label>
<input id="city" role="combobox" autocomplete="off" aria-autocomplete="list" aria-expanded="false" aria-controls="cities">
<ul id="cities" role="listbox" aria-label="Cities" hidden style="list-style:none;margin:4px 0;padding:0;border:1px solid #767676;max-width:220px">
  <li id="c1" role="option" aria-selected="false" hidden style="padding:2px 6px">Panama City</li>
  <li id="c2" role="option" aria-selected="false" hidden style="padding:2px 6px">Paris</li>
  <li id="c3" role="option" aria-selected="false" hidden style="padding:2px 6px">Parma</li>
  <li id="c4" role="option" aria-selected="false" hidden style="padding:2px 6px">Porto</li>
  <li id="c5" role="option" aria-selected="false" hidden style="padding:2px 6px">Lisbon</li>
</ul>
<p id="out" role="status" style="overflow-wrap:anywhere"></p>
<p>Type pa, then press the arrow keys and Enter, or click a city.</p>
<script>
const input = document.getElementById('city');
const list = document.getElementById('cities');
const out = document.getElementById('out');
const options = [...list.children];
let active = -1;
const shown = () => options.filter(o => !o.hidden);
function highlight() {
  options.forEach(o => { o.setAttribute('aria-selected', 'false'); o.style.background = ''; });
  const o = shown()[active];
  if (o) {
    o.setAttribute('aria-selected', 'true');
    o.style.background = '#dbeafe';
    input.setAttribute('aria-activedescendant', o.id);
  } else {
    input.removeAttribute('aria-activedescendant');
  }
}
function close() {
  list.hidden = true;
  input.setAttribute('aria-expanded', 'false');
  active = -1;
  highlight();
}
function pick(o) {
  input.value = o.textContent;
  close();
  out.textContent = 'Chosen: ' + o.textContent + '.';
}
function update() {
  const typed = input.value.trim().toLowerCase();
  options.forEach(o => { o.hidden = !typed || !o.textContent.toLowerCase().startsWith(typed); });
  active = -1;
  highlight();
  const count = shown().length;
  list.hidden = count === 0;
  input.setAttribute('aria-expanded', String(count > 0));
  out.textContent = typed ? 'The script found ' + count + (count === 1 ? ' match.' : ' matches.') : '';
}
input.addEventListener('input', update);
input.addEventListener('blur', close);
input.addEventListener('keydown', function (e) {
  if (list.hidden) {
    if (e.key === 'ArrowDown') { e.preventDefault(); update(); }
    return;
  }
  const count = shown().length;
  if (e.key === 'ArrowDown') { e.preventDefault(); active = (active + 1) % count; highlight(); }
  if (e.key === 'ArrowUp') { e.preventDefault(); active = active > 0 ? active - 1 : count - 1; highlight(); }
  if (e.key === 'Escape') close();
  if (e.key === 'Enter' && active >= 0) { e.preventDefault(); pick(shown()[active]); }
});
list.addEventListener('mousedown', function (e) {
  e.preventDefault();
  if (e.target.getAttribute('role') === 'option') pick(e.target);
});
</script>

Best practices

  • An editable combobox that autocompletes must carry aria-autocomplete, set to the value that matches what it does.
  • Don't change the value to signal a suggestion. The property isn't meant to show that one exists.
  • When a suggestion is selected automatically as the user types, keep DOM focus on the input and point aria-activedescendant at that suggestion. That model requires both.
  • Skip it on select-only comboboxes, where no text input is possible.

Frequently asked questions

Is aria-autocomplete related to the HTML autocomplete attribute?
Only loosely, since aria-autocomplete describes a widget's suggestions and autocomplete tells the browser what to autofill. The two have different features, though they overlap as ways to expose autocompletion support.
Does autocomplete=off cancel aria-autocomplete?
No, aria-autocomplete keeps its value alongside autocomplete=off. Browsers expose only the aria-autocomplete value in that case.
How does aria-autocomplete=both differ from list?
With both, aria-autocomplete adds an inline part to the list. One suggestion is selected automatically, and the text that completes it appears after the caret.
Does a list of recent searches count as aria-autocomplete=list?
No, aria-autocomplete stays none or absent when the suggestions don't depend on what was typed. A search field that lists the five most recent searches with no filtering is one such case.
Can a whole form take aria-autocomplete the way it takes autocomplete?
No, aria-autocomplete isn't supported on a form, unlike the HTML autocomplete attribute. That holds for the form element and for the form role.