Autocomplete

Suggestions from a datalist

Wrap a text input in <div class="autocomplete"> and give it a <datalist>, linked with the input's list attribute (or just placed in the wrapper). The script replaces the native suggestions with a styled list. Whatever is typed stays the value, even if it is not in the list. Without the script the native datalist still works.

In a field with a label and a hint

The label points at the input as usual. Arrow keys move through the suggestions and Enter picks one. With no active suggestion Enter keeps the typed text.

Pick a suggestion or type any place.

Show all suggestions on focus: data-min-length="0"

By default the list opens after one character (data-min-length="1"). Set it to 0 to show the suggestions as soon as the input is focused or clicked. data-limit caps how many are shown (default 8).

Remote suggestions: data-url

data-url on the wrapper is the search URL. The script sends GET url?q=typed (200 ms after the last key), aborts the previous request and caches the reply for each query. The reply is a JSON array of strings or of { value, label, text } objects. This page reads the static file data/cities.json, which ignores q, so the script also filters the list by the typed text. While loading, the input and the list have aria-busy.

Suggestions with a label and a line of text

A datalist option can carry a label (shown in the list) and data-text (a muted line under it). The value is what goes into the input. The matched part of the label is wrapped in <mark>. Text is searched too.

A custom source function

For any other lookup call tsunami.autocomplete.mount(el, { source }). The function gets the typed text and { signal } and returns (or resolves to) an array of strings or items. Here it completes an email domain.

Inside an input group, with an icon

Put an .input-group inside the wrapper. The list lines up with the whole box. data-size on the input (or the group) sets the height as usual.

Disabled and read-only

A disabled or read-only input shows no suggestions.

Autocomplete or Select?

Use Select when the value must be one of a fixed set of options (even with data-input or data-tags, the choice is an option of a native <select>). Use Autocomplete when the value is free text and the suggestions only save typing: a city, a product title, an email domain, a search box.

API

The script enhances every .autocomplete on load and when added later. The functions are only needed for a custom source.

Function Arguments Description
tsunami.autocomplete.mount(el, options) el: the .autocomplete wrapper, options.source: async (q, { signal }) => items Enhances the wrapper with a source function, replacing a previous setup. Items are strings or { value, label, text }. Replies are cached for each query and a new call aborts the previous one through signal.
tsunami.autocomplete.destroy(el) el: the wrapper Removes the list and the ARIA attributes, restores the input's list attribute and stops enhancing this wrapper.

Events

Event On Detail
tsunami:autocomplete input, bubbles { value, item }, where item is { value, label, text }. Fired when a suggestion is picked, not for typed text.
input, change input, bubbles Fired after a pick, so forms and the Save bar react as if the text had been typed. Typing fires the native events as usual.

Keyboard

Key Description
↓ ↑ Open the list if it is closed. In the list: move the active suggestion (aria-activedescendant).
Enter Picks the active suggestion. With none active it keeps the typed text (and submits the form as usual).
Escape Closes the list. The text stays.
Tab Leaves the input, closes the list and keeps the text.
Letters Filter the suggestions. Any text is a valid value.

Attributes

Attribute Value Description
data-url URL On the wrapper. Remote suggestions: GET url?q=, JSON array of strings or { value, label, text }.
data-min-length number (default 1) On the wrapper. Characters needed before the list opens. 0 shows all on focus.
data-limit number (default 8) On the wrapper. Most suggestions shown.
list datalist id On the input. The script removes it (the datalist stays the data source) and puts it back on destroy. A datalist inside the wrapper works too.
option[value] text In the datalist. The text put into the input.
option[label] text Shown in the list instead of the value. Matched and highlighted.
option[data-text] text A muted line under the title. Searched, not highlighted.
data-size sm, lg On the input, as for Input. The list follows the width of the input (or the input group).
aria-busy set by the script On the input and the listbox while remote suggestions load.

Accessibility

The input becomes an ARIA 1.2 combobox with list autocomplete: role="combobox", aria-autocomplete="list", aria-expanded, aria-controls (the listbox) and aria-activedescendant (the active option). The list is a role="listbox" labelled by the input's <label> or aria-label, with a role="option" for each suggestion. Focus never leaves the input. Give every input a label.