Skip to content

<u-datalist>

<u-datalist> lets you suggest values to a connected <input>. You can use it to make things like comboboxes, autosuggest, autocomplete, live search results, etc.

Quick intro:

Example

Install

bash
npm add -S @u-elements/u-datalist
bash
pnpm add -S @u-elements/u-datalist
bash
yarn add @u-elements/u-datalist
bash
bun add -S @u-elements/u-datalist
html
<script type="module" src="https://unpkg.com/@u-elements/u-datalist@latest/dist/u-datalist.js"></script>

Attributes and props

<u-datalist>

  • Attributes: all global HTML attributes such as id, class, data-
    • id must be identical to value of list attribute on associated <input>
    • Screen reader attributes required to meet WCAG §4.1.3:
      • data-sr-singular="%d hit" announces single hit
      • data-sr-plural="%d hits" announces multiple hits
      • Note: If <u-datalist> has no options, visible text will be announced instead (i.e. No results)
  • DOM interface: HTMLDataListElement
    • HTMLDataListElement.options returns HTMLCollectionOf<HTMLOptionElement>. However, note that there is inconsistency between Firefox and other browsers, regarding whether disabled options are included or not. To ensure access to all options, consider using .children instead.

<u-option>

  • Attributes: all global HTML attributes such as id, class, data-
    • disabled disables and hides the element if present. Note: Setting disabled="false" will not work as intended, as disabled is a boolean attribute you should provide or remove entirely.
    • label label sets text indicating the meaning of the option. If label isn't defined, its value is that of the element text content.
    • selected sets option selected state. Note: Setting selected="false" will not work as intended, as selected is a boolean attribute you should provide or remove entirely.
    • value represents the value to be filled into associated <input>. If value isn't defined, the value is taken from the text content of the option element.
  • DOM interface: HTMLOptionElement
    • HTMLOptionElement.defaultSelected returns true or false indicating state
    • HTMLOptionElement.disabled returns true of false reflecting the disabled attribute
    • HTMLOptionElement.form returns parent HTMLFormElement
    • HTMLOptionElement.index returns a number representing the position/index within the list of options
    • HTMLOptionElement.label sets or gets string of option label
    • HTMLOptionElement.selected sets or gets true or false indicating state
    • HTMLOptionElement.text sets og gets string of option text content
    • HTMLOptionElement.value sets og gets string of option value

Events

While <u-datalist> support all events, native datalist does not as it is rendered as part of the browser UI. Therefore, it's recommended to avoid binding events to <u-datalist> or <u-option> if you want to ensure native compatibility and future seamless opt-out.

Instead, you can detect option clicks by binding an input listener to <input>, and use the utility import { isDatalistClick } from '@u-elements/u-datalist':

HTML
<input type="text" list="my-list" />
<u-datalist id="my-list">
  <u-option>Option 1</u-option>
  <u-option>Option 2</u-option>
</u-datalist>
<script type="module">
  import { isDatalistClick } from '@u-elements/u-datalist';
  const input = document.querySelector('input')

  input.addEventListener('input', (event) => {
    if (isDatalistClick(event)) {
      // Event is triggered by user selecting an option in datalist
    } else {
      // Event is triggered by user typing in input
    }
  })
</script>
JSX
import { isDatalistClick } from '@u-elements/u-datalist';

const MyComponent = () => {
  const handleChange = (event) => {
    if (isDatalistClick(event.nativeEvent)) {
      // Event is triggered by user selecting an option in datalist
    } else {
      // Event is triggered by user typing in input
    }
  };

  return (
    <input type="text" list="my-list" onChange={handleChange} />
    <u-datalist id="my-list">
      <u-option>Option 1</u-option>
      <u-option>Option 2</u-option>
    </u-datalist>
  );
}

Styling

While <u-datalist> and <u-option> are styleable, native datalist and option elements are currently not. However, there is a possibility that the native elements may support styling in the future.

Styling with the display property

<u-datalist> and <u-option> are both rendered as display: block when visible, and are hidden by the hidden attribute. Styling with a display property will override the hidden attribute, thus disabling datalist show/hide and option filtering, unless you wrap your styling in a :not([hidden]) selector:

css
u-datalist:not([hidden]) {
  display: flex;
  /* Use :not([hidden]) if you wish to change u-datalist display
   * without disabling open/close */
}
u-option:not([hidden]) {
  display: flex;
  /* Use :not([hidden]) if you wish to change u-option display
   * without disabling filtering */
}

Styling option focus and selected state

<u-option> receive real focus on keyboard navigation, and a selected attribute on selection, which both can be utilized for styling:

css
u-option:focus {
  /* Focused option styling here */
}
u-option:not(:focus) {
  /* Un-focused option styling here */
}
u-option[selected] {
  /* Selected option styling here */
}
u-option:not([selected]) {
  /* Un-selected option styling here */
}

Example: Styling

Example: Popover

Using Popover API allows datalist to automatically render on top layer and cross boundries of scroll containers. Keep in mind that you're responsible for styling and positioning the datalist, just as you would with any other element using popover.

Example: Dynamic

Accessibility

Screen reader <datalist> <u-datalist>
VoiceOver (Mac) + Chrome ❌ Does not announce option count
VoiceOver (Mac) + Edge
VoiceOver (Mac) + Firefox ❌ Does not announce option count
VoiceOver (Mac) + Safari ❌ Does not announce option count
VoiceOver (iOS) + Chrome ❌ Does not announce options
VoiceOver (iOS) + Safari ❌ Does not announce options
Jaws (PC) + Chrome
Jaws (PC) + Edge
Jaws (PC) + Firefox
NVDA (PC) + Chrome
NVDA (PC) + Edge
NVDA (PC) + Firefox
Narrator (PC) + Chrome
Narrator (PC) + Edge
Narrator (PC) + Firefox ❌ Does not show options
TalkBack (Android) + Chrome
TalkBack (Android) + Firefox ❌ Does not show options
TalkBack (Android) + Samsung Internet

Specifications

Changelog

  • 1.0.4: Enhance Android compatibility
  • 1.0.3: Enhance JSDOM compatibility by not assigning view in FocusEvent
  • 1.0.2: Prevent error if removing <input> during input event
  • 1.0.1: Restore call to togglePopover
  • 1.0.0:
    • Removed support for isDatalistClick, syncDatalistState and getDatalistValue
    • Added support for data-nofilter
    • Fixed bug where VoiceOver + Safari announced incorrect amount of list items
    • Improved support for changes in disabled, hidden, label and value attributes
    • Improved popover support

Released under the MIT License