Overview
The autocomplete
component is a container for a ds-input
and ds-menu
used as the menu. This component only provides styling and does not provide any javascript required to implement auto-complete functionality.
Options
Basic usage
Non ARIA version
The following structure uses html hover
and focus
states to hide/reveal the list menu
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
<li>Squirtle</li>
<li>Venusaur</li>
<li>Wartortle</li>
</ul>
</div>
With ARIA attributes
The following structure uses role="combobox"
and aria-expanded
to hide/reveal the list menu. hover
and focus
will be ignored.
To show the list menu, set aria-expanded="true"
, to hide the list menu, set aria-expanded="false"
, aria-expanded=""
or aria-expanded
.
<!-- Click links to toggle aria-expanded state -->
<div class="ds-cols">
<a href="#" onClick="document.getElementById('example-combobox').setAttribute('aria-expanded', true); return false;"><code>aria-expanded="true"</code></a>
<a href="#" onClick="document.getElementById('example-combobox').setAttribute('aria-expanded', false); return false;"><code>aria-expanded="false"</code></a>
</div>
<!-- Autocomplete menu -->
<div class="ds-autocomplete">
<div
class="ds-input"
id="example-combobox"
role="combobox"
aria-autocomplete="list"
aria-controls="example-listbox"
aria-expanded="false"
aria-activedescendant=""
>
<input
type="text"
aria-label="Enter a Pokemon name"
autocomplete="off"
>
</div>
<ul class="ds-menu"
id="example-listbox"
role="listbox"
>
<li role="option">Anorith</li>
<li role="option">Arbok</li>
<li role="option">Arcanine</li>
<li role="option">Bulbasaur</li>
<li role="option">Butterfree</li>
<li role="option">Caterpie</li>
<li role="option">Charizard</li>
<li role="option">Charmander</li>
<li role="option">Charmeleon</li>
<li role="option">Squirtle</li>
<li role="option">Venusaur</li>
<li role="option">Wartortle</li>
</ul>
</div>
Click links to toggle aria-expanded state
Autocomplete menu
<!-- Click links to toggle aria-expanded state -->
<div class="ds-cols">
<a href="#" onClick="document.getElementById('example-combobox-2').setAttribute('aria-expanded', true); return false;"><code>aria-expanded="true"</code></a>
<a href="#" onClick="document.getElementById('example-combobox-2').setAttribute('aria-expanded', false); return false;"><code>aria-expanded="false"</code></a>
</div>
<!-- Autocomplete menu -->
<div class="ds-autocomplete">
<input
type="text"
aria-label="Enter a Pokemon name"
autocomplete="off"
class="ds-input"
id="example-combobox-2"
role="combobox"
aria-autocomplete="list"
aria-controls="example-listbox"
aria-expanded="false"
aria-activedescendant=""
>
<button class="ds-button --plain --addon --icon-ui-arrow-down"></button>
<ul class="ds-menu"
id="example-listbox"
role="listbox"
>
<li role="option">Anorith</li>
<li role="option">Arbok</li>
<li role="option">Arcanine</li>
<li role="option">Bulbasaur</li>
<li role="option">Butterfree</li>
<li role="option">Caterpie</li>
<li role="option">Charizard</li>
<li role="option">Charmander</li>
<li role="option">Charmeleon</li>
<li role="option">Squirtle</li>
<li role="option">Venusaur</li>
<li role="option">Wartortle</li>
</ul>
</div>
Click links to toggle aria-expanded state
Autocomplete menu
Height
By default the autocomplete will be the height of the menu contents. You can also restrict the overall height of the menu and cause the contents to scroll. If the menu can scroll, a shadow will be added at the bottom/top of the menu to indicate the menu is scrollable.
There are two ways to restrict the height.
Restrict the height to the viewport
Adding the class --viewport
to the menu will restrict the height to 100vh
- --ds-header-height
(usually 100vh - 60px) and force the menu to scroll.
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --viewport">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
<li>Squirtle</li>
<li>Venusaur</li>
<li>Wartortle</li>
</ul>
</div>
Restrict the height to a specific size
You can restrict the height by changing the --ds-menu-height
value (either in a css file, inline style tag or via javascript).
let myCustomMenu = document.getElementById("customMenu")
myCustomMenu.style.setProperty('--ds-menu-height', "400px")
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu" style="--ds-menu-height: 200px;">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
<li>Squirtle</li>
<li>Venusaur</li>
<li>Wartortle</li>
</ul>
</div>
Loading state
A details can be triggered to display a loading state - where the details contents are hidden, and the replaced by a centered spinning loading icon – without affecting the dimensions of the details.
Additional options are available using Message state
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --loading">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --loading --loading-refresh">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --loading --icon-radar">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
Message state
Message state extends and can be used with Loading state. If a details has a data-attribute data-ds-message
the details contents are hidden, and replaced by an icon and the text of the data-ds-message
.
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu" data-ds-message="Message text">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --loading" data-ds-message="Message text">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --icon-rocket" data-ds-message="Message text">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu --loading --icon-compass" data-ds-message="Message text">
<li>Anorith</li>
<li>Arbok</li>
<li>Arcanine</li>
<li>Bulbasaur</li>
<li>Butterfree</li>
<li>Caterpie</li>
<li>Charizard</li>
<li>Charmander</li>
<li>Charmeleon</li>
</ul>
</div>
Input Icons
See ds-input icons.
List icons
See ds-menu icons.
List descriptions
Accessibility
Role and ARIA attributes
Recommendations for implementing ds-autocomplete to meet W3C WAI-ARIA guidelines are as follows:
Autocomplete container (div)
<div class="ds-autocomplete">
...
</div>
Autocomplete combobox (div)
<div class="ds-input"
role="combobox"
aria-autocomplete="list"
aria-controls="[ID]"
aria-expanded="[true|false]"
aria-activedescendant="[ID]"
>
...
</div>
Element | Attribute | Required | Type | Description |
---|---|---|---|---|
<div> |
role="combobox" |
Required | Static | Indicates the div is a combobox |
aria-autocomplete |
Required | Static | Indicates the combobox is an autocomplete list | |
aria-controls="[ID]" |
Required | Static | Refers to the ID of the autocomplete list | |
aria-expanded="[true|false]" |
Required | Dynamic | 1. Set to true when the list is open 2. Removed or set to false when the list is closed |
|
aria-activedescendant |
Required | Static | Refers to the ID of the focused item in the autocomplete list |
Autocomplete textbox (input)
<div class="ds-input">
<input type="text"
aria-label="[string]"
autocomplete="off"
>
</div>
Element | Attribute | Required | Type | Description |
---|---|---|---|---|
<input type="text"> |
aria-label |
Required | Static | Descriptive label |
autocomplete="off" |
Recommended | Static | Disable default browser autocomplete |
Autocomplete list (ul)
<ul class="ds-menu"
id="[ID]"
role="listbox"
>
<li
id="[ID]"
role="option"
aria-selected="[true|false]">
...
</li>
</ul>
Element | Attribute | Required | Type | Description |
---|---|---|---|---|
<ul> |
id |
Required | Static | 1. Unique ID 2. Related to the combobox aria-controls |
role |
Required | Static | Indicates the element is an autocomplete list | |
<li> |
id |
Required | Static | 1. Unique ID 2. Related to the combobox aria-activedescendant |
role |
Required | Static | Indicates the element is an autocomplete list option | |
aria-selected |
Required | Dynamic | Indicates when the option is selected |
Keyboard access
The following keyboard controls must be implemented to provide accessible keyboard access.
Autocomplete textbox (input):
Default browser support for entering and editing text inputs applies, such as delete
, left/right arrow
.
Key | Required | Support | Description |
---|---|---|---|
Enter |
Required | Javascript | 1. If a list item is selected, sets the value of the textbox to the selected option. 2. Closes the autocomplete list. |
Escape |
Required | Javascript | 1. Closes the autocomplete list. 2. Clears the textbox. |
Down Arrow |
Required | Javascript | 1. If no list item is focused, moves focus to the first list item. 2. If a list item is focused, moves to the next item. 3. If the last item is focused, moves to the first list item. |
Up Arrow |
Required | Javascript | 1. If no list item is focused, moves focus to the last list item. 2. If a list item is focused, moves to the previous item. 3. If the first item is focused, moves to the last list item. |
Printable Characters |
Required | Javascript | 1. Types the character in the textbox. 2. Filters the items in the Autocomplete list. 3. Selects the first matching item. |
Autocomplete list (ul):
Key | Required | Support | Description |
---|---|---|---|
Enter |
Required | Javascript | 1. If a list item is selected, sets the value of the textbox to the selected option. 2. Closes the autocomplete list. 3. Move focus to the textbox. |
Escape |
Required | Javascript | 1. Closes the autocomplete list. 2. Clears the textbox. 3. Sets focus to the textbox. |
Down Arrow |
Required | Javascript | 1. Moves to the next item. 2. If the last item is selected, moves to the first list item. |
Up Arrow |
Required | Javascript | 1. Moves to the previous item. 2. If the first item is selected, moves to the last list item. |
Left Arrow |
Required | Javascript | 1. Move focus to the textbox. 2. Moves cursor one character to left. |
Right Arrow |
Required | Javascript | 1. Move focus to the textbox. 2. Moves cursor one character to right. |
Home |
Required | Javascript | 1. Move focus to the textbox. 2. Moves cursor to beginning of the textbox. |
end |
Required | Javascript | 1. Move focus to the textbox. 2. Moves cursor to end of the textbox. |
Printable Characters |
Required | Javascript | 1. Move focus to the textbox. 2. Types the character in the textbox. |
Examples
Autocomplete list
<div class="ds-field">
<label for="example-1" class="ds-text --label">Field test</label>
<div class="ds-autocomplete" role="combobox" aria-haspopup="listbox" aria-owns="example-1-list">
<div class="ds-input">
<input type="text" name="example-1-input" id="example-1-input" aria-autocomplete="list" aria-controls="example-1-list">
</div>
<ul id="example-1-list" class="ds-menu">
<li role="option" id="option-anorith">Anorith</li>
<li role="option" id="option-arbok">Arbok</li>
<li role="option" id="option-arcanine">Arcanine</li>
<li role="option" id="option-bulbasaur">Bulbasaur</li>
<li role="option" id="option-butterfree">Butterfree</li>
<li role="option" id="option-caterpie">Caterpie</li>
<li role="option" id="option-charizard">Charizard</li>
<li role="option" id="option-charmander">Charmander</li>
<li role="option" id="option-charmeleon">Charmeleon</li>
<li role="option" id="option-squirtle">Squirtle</li>
<li role="option" id="option-venusaur">Venusaur</li>
<li role="option" id="option-wartortle">Wartortle</li>
</ul>
</div>
</div>
Autocomplete list with grid content
<div class="ds-autocomplete">
<div class="ds-input">
<input type="text" name="test-field">
</div>
<ul class="ds-menu">
<li data-ds-value="019281919281">
<dl class="ds-details--vertical">
<dt>Sim ID: 019281919281</dt>
<dd>
<div>My SIM</div>
<div class="ds-text --small --success --icon-online">Online</div>
</dd>
</dl>
</li>
<li data-ds-value="9381273784832">
<dl class="ds-details--vertical">
<dt>Sim ID: 9381273784832</dt>
<dd>
<div>My Other SIM</div>
<div class="ds-text --small --alert --icon-online">Offline</div>
</dd>
</dl>
</li>
<li data-ds-value="9381273784719">
<dl class="ds-details--vertical">
<dt>Sim ID: 9381273784719</dt>
<dd>
<div>My Other SIM</div>
<div class="ds-text --small --alert --icon-online">Offline</div>
</dd>
</dl>
</li>
</ul>
</div>
Autocomplete with grid content and preview
This implementation is based on the ARIA Editable Combobox with Grid Popup
<div class="ds-field">
<!-- Label -->
<label class="ds-text --label" for="autocomplete-filter">API</label>
<!-- Autocomplete -->
<div class="ds-autocomplete">
<!-- Optional: This should only show the currently selected option data -->
<dl class="ds-details --vertical">
<dt class="--no-case">{{ selected-option-name }}</dt>
<dd>
<div class="ds-text --small">{{ selected-option-description }}</div>
</dd>
<dd>
<div class="ds-cols --middle --spaced --indent-small">
<div class="ds-tag --outline --color-blue">
<span>{{ selected-option-method }}</span>
</div>
<div class="ds-text --code --small --color-blue">{{ selected-option-url }}</div>
</div>
</dd>
</dl>
<!-- Filter -->
<div class="ds-input --icon-search">
<input
type="text"
id="autocomplete-filter"
role="combobox"
aria-autocomplete="list"
aria-activedescendant="{{ active descendant id }}"
aria-controls="autocomplete-listbox"
aria-expanded="{{ true|false }}"
aria-haspopup="grid"
/>
</div>
<!-- Button -->
<button
class="ds-button --plain --addon --icon-ui-arrow-down --hide-label --top-left"
aria-controls="autocomplete-listbox"
aria-expanded="{{ true|false }}"
aria-label="Show API options"
>
<span>Show API options</span>
</button>
<!-- Grid -->
<div
class="ds-menu"
id="autocomplete-listbox"
role="grid"
aria-hidden="{{ true|false }}"
style="--ds-menu-max-height: 420px"
>
<ul
role="group"
aria-labelledby="{{ group-id }}"
>
<li role="presentation" class="ds-text --bold" id="{{ group-id }}">
{{ option name }}
</li>
<li
role="row"
id="{{ option-id }}"
aria-selected="{{ true|false }}"
class="{{ if --focused }}"
>
<dl class="ds-details --vertical">
<dt
class="--no-case"
role="gridcell"
>{{ option-name }}</dt>
<dd>
<div
class="ds-text --small"
role="gridcell"
>{{ option-description }}</div>
</dd>
<dd>
<div class="ds-cols --middle --spaced --indent-small">
<div
class="ds-tag --outline --color-blue"
role="gridcell"
>
<span>{{ option-method }}</span>
</div>
<div
class="ds-text --code --small --color-blue"
role="gridcell"
>{{ option-url }}</div>
</div>
</dd>
</dl>
</li>
</ul>
</div>
</div>
</div>