✎ Technique: Autocomplete input controls

Autocomplete widgets can be helpful for accessibility because they can make it easier to enter text by providing suggestions based on the characters initially typed. This particularly helps people who find typing more difficult and people who may be susceptible to spelling mistakes.

Creating an accessible integrated autocomplete widget is a complex process. You need to ensure that screen-reader users are notified when the list of suggestions appears and that they can enter the list and select an option using the keyboard.

One solution is to use an ARIA live region to announce when autocomplete options are available, and to create a listbox containing the options that appear as the user types, which is controlled with the up and down arrow keys.

Example

For this example, we’re manually implementing autocomplete for a “choose your pet” input. To begin with, the markup should look like this:


<form>
  <label for="pets">Choose a pet</label>
  <div class="suggestions-container">
    <div class="suggestions-help" role="status" aria-live="polite"></div>
    <input id="pets" data-suggest type="text" autocomplete="off" aria-autocomplete="list">
    <div class="suggestions"></div>
  </div>
</form>

Note the .suggestions-help live region and .suggestions, which will need to be populated with the listbox (where applicable) on the input event. Also note autocomplete="off", which suppresses native autocomplete functionality that could get in the way of our custom UI. The ARIA property of aria-autocomplete="list" replaces the semantics so that “autocomplete” is announced on focus.

$('[data-suggest]').on('input', function() {
  // handle suggestions here
});

In the code editor example you’ll see an array of pet types. As the user types, the input event listens for changes and compares the entered value to each item in the pets array. If there are matches, the first thing the script does is alert the user using the live region:


if (!_.isEmpty(suggestions)) {
      $('.suggestions-help', scope).text(
      'There are '+suggestions.length+' suggestions. Use the up and down arrows to browse.');
    }
  }

This tells the user how many suggestions are available (suggestions.length) and—importantly—how to choose from those suggestions. The newly populated listbox is made focusable using tabindex="0", and it is focused if the user presses the down arrow key. In this state, list’s markup looks like this:


<div role="listbox" tabindex="0" aria-activedescendant="pets-0">
  <div id="pets-0" role="option" tabindex="-1">hamster</div>
  <div id="pets-1" role="option" tabindex="-1">hermit crab</div>
  <div id="pets-2" role="option" tabindex="-1">horse</div>
</div>

Note the listbox role and that each child of the listbox has a role of option. This is the only way that a listbox will provide the correct information to screen readers.

Also note that the listbox has a property called aria-activedescendant. This keeps track of the “active” option as the user searches through the options with the up and down keys. It uses the current option’s id as its value, and that value is updated with JavaScript. That way, whenever a new option is made active, screen readers should announce the option’s text, such as “cat,” and the index of the option, such as “two of three.”

To choose the selected list option, the user can hit enter, which clears the list of options from the listbox and refocuses the input field:


if (e.keyCode == 13) {
  e.preventDefault();
  e.stopPropagation();
  $('#pets').focus();
}

Code editor

See this custom autocomplete implementation in a code editor. Try operating it with just your keyboard, with a screen reader running at the same time.