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>
  • 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.

If you need to detect click on options or require a more fine-grained API — including comboboxbeforeselect and comboboxafterselect events — we recommend using <u-combobox>, which extends <u-datalist> with several useful features →

Note:

Pressing inside <u-datalist> keeps focus on the <input>, like a native datalist, by cancelling mousedown inside the list. Focusable elements placed inside <u-datalist> (i.e. a "Load more" button) therefore do not receive focus on click, but still receive the click event.

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.

Screen reader texts ​

Screen reader texts are set through CSS custom properties. They inherit, so a translation can be set once for a page or a language, i.e. :lang(nb) { --sr-plural: "%d treff" }.

  • --sr-singular announces single hit. Defaults to "%d hit"
  • --sr-plural announces multiple hits. Defaults to "%d hits"
  • --sr-of separates "number of total" in announcements. Defaults to "of"
  • If <u-datalist> has no options, visible text will be announced instead (i.e. No results)

Note:

If you are using a strict Content Security Policy (CSP), inline style HTML attributes are blocked. Set the properties in your CSS, or through element.style.setProperty('--sr-of', '"New string"') (note that the string must be quoted) or a framework style binding.

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 a data-activedescendant attribute when active on keyboard navigation (focus stays on the <input>, using aria-activedescendant), and a selected attribute on selection, which both can be utilized for styling:

css
u-option[data-activedescendant] {
  /* Focused option styling here */
}
u-option:not([data-activedescendant]) {
  /* 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 ​

Server side rendering ​

You can server side render <u-datalist> by using Declarative Shadow DOM. Styling and markup needed is exported as UHTMLDataListShadowRoot. Example:

TS
import { UHTMLDataListShadowRoot } from '@u-elements/u-datalist';

const renderToStaticMarkup = (options: string) =>
 `<u-datalist>
   ${UHTMLDataListShadowRoot}
   ${options}
 </u-datalist>`

Hydration: Attributes are dynamically added in the browser, so the client DOM will differ from the server markup. Use suppressHydrationWarning in React, data-allow-mismatch="attribute" in Vue, or similar for your framework on these elements, to avoid hydration warnings.

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 ⚠️ Shows 3 unannounced options only in keyboard ✅
VoiceOver (iOS) + Safari ⚠️ Shows 3 unannounced options only in keyboard ✅
Jaws (PC) + Chrome ✅ ✅
Jaws (PC) + Edge ✅ ✅
Jaws (PC) + Firefox ✅ ✅
NVDA (PC) + Chrome ✅ ✅
NVDA (PC) + Edge ✅ ✅
NVDA (PC) + Firefox ✅ ✅
Narrator (PC) + Chrome ✅ ⚠️ Requires user to deactivate "scan mode"
Narrator (PC) + Edge ✅ ⚠️ Requires user to deactivate "scan mode"
Narrator (PC) + Firefox ⚠️ Requires user to press AltKey + ArrowDown twice ⚠️ Requires user to deactivate "scan mode"
TalkBack (Android) + Chrome ✅ ✅ Shows/hides based on focus
TalkBack (Android) + Firefox ❌ Does not show options ✅ Shows/hides based on focus
TalkBack (Android) + Samsung Internet ✅ ✅ Shows/hides based on focus

Specifications ​

Notes ​

  • Axe automated tests does not allow role="combobox" on <input> with list, since combobox is implied. Explicit role is needed to secure compatibility in all environments, so set "aria-allowed-role" for Axe to silence the error if needed.

Changelog ​

  • 3.0.2: data-nofilter="false" no longer disables filtering, increasing compatibility with frameworks where data-attributes are always written
  • 3.0.1: Allow overwriting internal display: none on hidden, disabled or aria-hidden="true" elements even when using CSS layers
  • 3.0.0:
    • Screen reader texts are set through --sr-* CSS instead of data-sr-* attributes
    • aria-posinset and aria-setsize on options is now supported
    • Alignment with native <datalist>:
      • beforeinput is no longer dispatched when picking
      • Holding Enter on an option picks it once instead of on every key repeat
      • ArrowUp without an active option moves to the last option
      • Pressing inside the list keeps focus on the input, so focusable elements inside the list do not receive focus on click
      • Inputs sharing a list keep their combobox attributes when focus moves between them
    • Framework typings for Qwik 1, Qwik 2 and Preact 11 uses HTMLElementTagNameMap
  • 2.0.3: Allow Shift key events combined with a letter to open datalist
  • 2.0.2: Instant setup aria-expanded attribute to make AXE tests happy
  • 2.0.1: Use document.activeElement instead of .matches(":focus-within") for JSDOM testing compatibility
  • 2.0.0: Plays nice with strict CSP, correctly handles foucs-before-mount and click-to-open, and uses aria-activedecendant as support is now sufficient
  • 1.1.1: Speed up popover check
  • 1.1.0: Allows changing tag name when extending UHTMLDataListElement class
  • 1.0.14: Enable declarative shadow root support and export UHTMLDataListShadowRoot for easier server side rendering
  • 1.0.13: Prevent opening if inputmode="none"
  • 1.0.12: Prevent ESC from closing dialog when datalist is open
  • 1.0.11: Respects input disabled and readonly and moves caret to end of text on arrow up
  • 1.0.10: Automatically update popovertarget and aria-controls of input when id of datalist changes
  • 1.0.9: Fix issue when mousedown inside, but mouseup outside datalist prevented blur events
  • 1.0.8: Only accept aria-hidden="true", hidden or disabled to hide options for improved performance
  • 1.0.7: Performance optimize by grouping read and write operations
  • 1.0.6: Prevent announce hits count if closed
  • 1.0.5: Improved support for nested DOM and CSS-hiding options
  • 1.0.4: Improved Android compatibility by not relying on focus events
  • 1.0.3: Improved JSDOM compatibility by not assigning view in FocusEvent
  • 1.0.2: Fix error when consumer removes <input> element during input event
  • 1.0.1: Improved popover compatibility
  • 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