Combobox

An enhancement of a textbox
Select phone model item specifics

Introduction

An enhanced textbox that allows free text input, selection from a predefined list of values, or a combination of both. Hence the name "combobox".

A combobox allows free text input and selection from a predefined list of values. It may optionally have autocomplete behaviour.

A readonly combobox (i.e. selection from a predefined list only) is often used as a custom facade for the HTML select element (which has limited styling capabilities with CSS). This guide, however, is primarily concerned with the editable version of the combobox.

Updated: May 23rd, 2019

NOTE: we are in the process of restructuring the Autocomplete, Combobox, Listbox and Select patterns based on latest WCAG Authoring Practices 1.1. Please bear with us.

Terminology

widget: the pattern as a whole, comprised of the following distinct parts

textbox: stores and displays the form value

flyout: the overlay that contains a listbox

listbox: listbox containing options

button: expands or collapses the listbox overlay (optional)

collapsed/expanded: state of listbox overlay

Configuration

  • autoSelect: a combobox with autoSelect will automatically select and fill the textbox value when user cycles through listbox options. Otherwise, SPACEBAR or ENTER key is required to manually select an option.

Working Example

You can take a look at the combobox pattern in action on our examples site.

You can get an idea of the required markup structure by viewing our bones project.

Best Practices

Each row in the list of options allows only a single action, and that action is always to set the value of the textbox. It is not possible, at the time of writing, to have multiple actions per row, e.g. select, edit and delete.

Combobox is not intended for any kind of single-select or multi-select state. Again, the purpose is simply to edit or set the value of a textbox.

Interaction Design

Keyboard

When the combobox receives focus, the listbox should expand to show all options.

Pressing ENTER or SPACEBAR on the button (if present) must toggle the listbox expanded state.

Combobox keyboard focus will appear to be in two places at the same time (the textbox and the listbox). In actual fact, keyboard focus always stays on the textbox. The aria-activedescendant property controls the pseudo-focus inside of the listbox.

With listbox expanded, pressing DOWN-ARROW and UP-ARROW keys must navigate pseudo-focus (i.e. the ARIA active-descendant) through the list of options.

Pressing SPACEBAR must always enter a blank space in the textbox.

For a combobox with autoSelect, changing the highlighted option will automatically fill the textbox with that option.

For a combobox without autoSelect, changing the highlighted option must not automatically fill the texbox; ENTER key is required to manually select the option.

Pressing ENTER key while an option is highlighted must collapse the listbox. For a combobox with autoSelect the form will be submitted. For a combobox without autoSelect the form must not be submitted.

Pressing ESC key while an option is highlighted must collapse the listbox.

Screen Reader

The screen reader will announce the input as "text edit", "combobox" or words to those effect, depending on level of ARIA support.

Mouse and Touch

Clicking or tapping the button (if present) must toggle the listbox display.

Clicking or tapping an option will fill the textbox with that value and collapse the listbox without triggering a form submit.

Developer Guide

Combobox is a good example of progressive enhancement. Until JavaScript is loaded or initalised, the textbox operates as a regular textbox. For example, a user can still enter and submit a value using the plain old textbox. The ability to choose a value from a list of pre-defined options is considered the enhancement that will be available with JavaScript.

Textbox

We start with a label and textbox:

<span class="combobox" id="combobox-0">
<label for="combobox-0-input">Game Console</label>
<input id="combobox-0-input" name="console" type="text" placeholder="Playstation 4, Xbox One, etc."/>
<!-- button will go here -->
<!-- listbox options will go here -->
</span>

We have added our elements inside of a .combobox wrapper element. This wrapper acts as our module root and hook for CSS & JavaScript.

Remember: the textbox does not yet have a role of combobox, it is added later with JavaScript.

A button and listbox elements will be appended to this wrapper. It is up to you whether you wish to render these elements server-side or client-side. There are pros and cons to both approaches, which we will discuss below.

Button (optional)

The button should be placed *between* the textbox and listbox in the DOM. This button allows mouse and touch users to expand the combobox.

<button type="button" disabled>Expand</button>

It is sometimes useful to render this button on the server to avoid a flash of content or layout issues. If rendered on the server, you may with to set the button to a disabled state. This is because the button is useless without JavaScript. The button can be enabled after all client-side initialisation for the widget is complete.

Description

At the time of writing, screen readers don't do a good job of letting the user know how to interact with a combobox or how many options it contains. To solve this problem, we can add offscreen text.

UPDATE: It seems that screen reader support is now much better for conveying the combobox role, and so way may now be able to drop the need for this additional description.

<span id="combobox-0-description" class="clipped">Combobox has 6 options. Use arrow keys to navigate options, and ENTER key to select.</span>

It is strongly recommended to add this description element on the client-side, and only after we are sure the widget is fully functional.

Next, in order for the screen reader to announce this description, we must add an aria-describedby attribute to the combobox. Again, this should be added using JavaScript on the client-side.

<input id="combobox-0-input" name="console" type="text" placeholder="Playstation 4, Xbox One, etc." aria-describedby="combobox-0-description" />

Now the screen reader announces the description whenever focus lands on the combobox.

Live Description

If the number of options changes based on the value of the combobox (i.e. filtering occurs) this new information must be related to assistive technology. The description element can be converted to a live region in order to achieve this.

<span id="combobox_0-description" class="clipped" role="status" aria-live="polite">Combobox has 6 options. Use up and down keys to navigate.</span>

The new attributes are role=status and aria-live=polite.

Note: aria-live attribute is technically redundant here, because status role has an implicit aria-live setting of polite. We add it to double-up on our support for older browsers and AT that might not support the status role.

Listbox

After the description, we add the listbox of options. The listbox may render on the server or the client. Like the disabled button above, it is wise to put the listbox in a hidden state if rendering on the server. To do so, use the hidden attribute.

<div class="combobox__overlay" hidden>
<ul id="combobox_0-listbox" role="listbox">
<li role="option" id="nid-0">Playstation 3</li>
<li role="option" id="nid-1">Playstation 4</li>
<li role="option" id="nid-2">Xbox 360</li>
<li role="option" id="nid-3">Xbox One</li>
<li role="option" id="nid-4">Wii</li>
<li role="option" id="nid-5">Wii U</li>
</ul>
<!-- status goes here -->
</div>

Using JavaScript we now convert the textbox to a combobox, by adding role=combobox. We also create the properties and state that connect the combobox to the listbox:

<input id="combobox-0-input" name="console0" type="text" placeholder="Playstation 4, Xbox One, etc." role="combobox" aria-expanded="false" autocomplete="off" aria-owns="combobox_0-listbox" aria-describedby="combobox_0-instructions">

The new attributes are role, aria-expanded, autocomplete and aria-owns.

Keyboard and Screen Reader Navigation

Our elements are now in place, but how does a keyboard user navigate to the options? We cannot use TAB key because focus must stay on the combobox (so that user can type and enter their own value). As with most complex widgets, the answer lies in the arrow keys. Up and down arrow keys are the way to select our combobox options.

If focus must remain on the combobox, how then do we also have focus on the listbox options? The answer is that we don't. Focus always remains on the combobox and instead we have a kind of pseudo-focus on the options.

How does the screen reader know where this pseudo-focus is?

Active Descendant

We call the option with pseudo-focus the "active descendant". And guess what, there is an ARIA attribute for this called aria-activedescendant. This attribute is placed on the combobox element. The attribute value is the ID of the currently active (pseudo-focussed) option. This allows assistive technology such as a screen reader to programmatically determine

To make all of this easier, we recommend using a plugin such as makeup-active-descendant. After your HTML structure is in place, simply initialise the plugin on the widget and up/down arrow keys will update the necessary states. Use CSS to style the active descendant in any way you like.

ARIA Reference

This section gives an overview of ARIA usage, within the context of this pattern.

role=combobox

This attribute changes the role of the text input from textbox to combobox. We recommend applying this attribute on the client-side with JavaScript.

role=listbox

The list of suggestions has a role of listbox

role=option

Each listbox item has a role of option.

aria-describedby

This property connects the combobox to the offscreen description/instructions.

aria-owns

This property creates a programmatic hierarchy in the accessibility tree for the combobox and the listbox.

aria-expanded

Conveys the expanded state of the combobox.

aria-label

Provides the expand/collapse button with an accessible label, in the case where it has no visible text (i.e. an icon button).

Changelog

  • May 23rd 2019: Removed role="application" hack. Removed live region workaround for VoiceOver's lack of support for active-descendant (support now seems much better).

  • Apr 29th, 2019: Minor updates regarding how the listbox can be expanded/collapsed

  • Oct 12th, 2017: Added offscreen live-region to combat new issue with VoiceOver

  • May 13th, 2016: Developer Guide added

  • Feb 22nd, 2016: Issue with Safari and Voiceover not announcing combobox options is now fixed.