NOTE: we are in the process of restructuring the Combobox, Listbox and Select patterns. Please bear with us.
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 comes in two flavors: editable and readonly.
A readonly combobox (i.e. selection from a list only) is often used as a custom facade for the HTML select element (which has limited styling capabilities with CSS).
An editable combobox allows free text input and selection from a predefined list of values. It may optionally have autocomplete behaviour.
This guide, for now, is primarily concerned with the editable version of the combobox.
- Feb 22nd, 2016
- Issue with Safari and Voiceover not announcing combobox options is now fixed.
- May 13th, 2016
- Developer Guide added
- Oct 12th, 2017
- Added offscreen live-region to combat new issue with VoiceOver
- the pattern as a *whole*, comprising of a textbox and listbox (and optional button)
- stores the form value. Can be editable or readonly.
- overlay containing options
- expands or collapses the listbox overlay
- hidden/visible state of listbox overlay
You can take a look at the editable combobox pattern in action on our examples site.
You can get an idea of the required markup structure by viewing our bones project.
Each row in the list of options allows only a single action. It is not possible to have multiple actions per row, e.g. select, edit and delete.
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 keyboard focus on the combobox, down arrow key will expand the listbox.
Alternatively, you may wish to auto-expand the listbox as soon as focus lands on the combobox. In this case, you may wish to exclude the button.
There is no strict requirement for the combobox button to be in the keyboard tabindex. This is because the listbox can be expanded using the down arrow key.
Up and down arrow keys navigate the list of options.
Pressing ENTER key on any option will set the combobox value and close the list of options.
Pressing ESC key will close the listbox if visible, otherwise clear the textbox value.
The screen reader will announce the input as 'text edit', 'combobox' or words to those effect, depending on level of ARIA support.
When keyboard focus is on the textbox, screen reader should announce "Combobox has n options available. Use arrow keys to navigate options, and ENTER to select" - or words to those effect. Typically this instructional text is not always provided by screen readers and so may need to be added by the web developer.
Mouse and Touch
Clicking or tapping the combobox button will toggle the listbox display.
Alternatively, you may wish to auto-expand the listbox when tapping inside the combobox input.
Clicking or tapping an option will fill the combobox with that value. For long forms, the list of options must close without triggering form submit.
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 --> <!-- description will go here --> <!-- listbox options will go here --> </span>
We have added our elements inside of a
A button, description 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.
The button should immediately follow the textbox. This button allows mouse and touch users to expand the combobox.
<button type="button" disabled>Expand</button>
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.
<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
<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.
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
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.
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
<div class="combobox__overlay"> <ul id="combobox_0-listbox" role="listbox" hidden> <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>
<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
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 characters). As with mosts 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?
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 jquery-active-descendant. After your HTML structure is in place, simply activate 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.
Problem #1: VoiceOver and Active Descendant
Historically, the Safari and VoiceOver combination has lacked support for the
aria-activedescendant state, and this situation is still true today. To workaround this issue we create another offscreen live region and append it to the overlay.
<div class="combobox__overlay"> <ul id="combobox_0-listbox" role="listbox" hidden> <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> <div class="flyout__status" aria-live="assertive" aria-atomic="true"> <span></span> </div> </div>
When there is no active descendant, the nested span remains empty. When there is an active descendant, we populate the nested span with the value of that option, thus forcing VoiceOver to announce the live region.
<div class="combobox__status" aria-live="assertive" aria-atomic="true"> <span>Option: Xbox 360</span> </div>
This live region can of course be hidden offscreen, using the
For screen readers that do support active descendant, this does mean that the value is going to be announced twice, something like:
"Xbox 360 [pause] Option 3 of 6 [pause] Option Xbox 360"
Problem #2: JAWS Keyboard Navigation
At the time of writing we have found that role of combobox does not force JAWS into application mode. This means that ARROW keys will be intercepted by JAWS, thus making UP/DOWN arrow key navigation problematic. In order to force JAWS into application mode, we apply a wrapper element with
<span role="application"> <span class="combobox" id="combobox-0"> <!-- label, input, description, etc --> </span> </span>
We hope that this workaround will only be a temporary measure until we see better support for combobox role in JAWS.
This section gives an overview of ARIA usage, within the context of this pattern.
We have found that this wrapper role is necessary for correct behaviour of keyboard accessibility in JAWS and IE.
This attribute changes the role of the text input from
This role converts the offscreen text description/instructions into an ARIA live region.
The list of suggestions has a role of listbox
Each listbox item has a role of option.
This property connects the combobox to the offscreen description/instructions.
This property connects the combobox to the listbox.
Conveys the expanded state of the combobox.
Provides the expand/collapse button with an accessible label, in the case where it has no visible text (i.e. an icon button).