<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:
- Use
<u-option>as direct child elements - these will show the suggestions while typing - Use matching
idon<u-datalist>andlistattribute on<input>to connect - Use
data-nofilterto prevent filtering - Use
data-*attributes to translate screen reader announcements - Use
popoverattribute to activate Popover API - Want to show suggestions from a data source? See u-combobox →
- MDN Web Docs: <datalist> (HTMLDatalistElement) / <option> (HTMLOptionElement)
Example
<label for="my-input">
Choose flavor of ice cream
</label>
<input id="my-input" list="my-list" />
<u-datalist id="my-list" data-sr-singular="%d flavor" data-sr-plural="%d flavours">
<u-option>Coconut</u-option>
<u-option>Strawberries</u-option>
<u-option>Chocolate</u-option>
<u-option>Vanilla</u-option>
<u-option>Licorice</u-option>
<u-option>Pistachios</u-option>
<u-option>Mango</u-option>
<u-option>Hazelnut</u-option>
</u-datalist>
<style>
/* Styling just for example: */
u-option[selected] { font-weight: bold }
</style>
Install
npm add -S @u-elements/u-datalistpnpm add -S @u-elements/u-datalistyarn add @u-elements/u-datalistbun add -S @u-elements/u-datalist<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-idmust be identical to value oflistattribute on associated<input>- Screen reader attributes required to meet WCAG §4.1.3:
data-sr-singular="%d hit"announces single hitdata-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:
HTMLDataListElementHTMLDataListElement.optionsreturnsHTMLCollectionOf<HTMLOptionElement>. However, note that there is inconsistency between Firefox and other browsers, regarding whetherdisabledoptions are included or not. To ensure access to all options, consider using.childreninstead.
<u-option>
- Attributes: all global HTML attributes such as
id,class,data-disableddisables and hides the element if present. Note: Settingdisabled="false"will not work as intended, asdisabledis a boolean attribute you should provide or remove entirely.labellabel sets text indicating the meaning of the option. Iflabelisn't defined, its value is that of the element text content.selectedsets option selected state. Note: Settingselected="false"will not work as intended, asselectedis a boolean attribute you should provide or remove entirely.valuerepresents the value to be filled into associated<input>. Ifvalueisn't defined, the value is taken from the text content of the option element.
- DOM interface:
HTMLOptionElementHTMLOptionElement.defaultSelectedreturnstrueorfalseindicating stateHTMLOptionElement.disabledreturnstrueoffalsereflecting thedisabledattributeHTMLOptionElement.formreturns parentHTMLFormElementHTMLOptionElement.indexreturns anumberrepresenting the position/index within the list of optionsHTMLOptionElement.labelsets or getsstringof optionlabelHTMLOptionElement.selectedsets or getstrueorfalseindicating stateHTMLOptionElement.textsets og getsstringof option text contentHTMLOptionElement.valuesets og getsstringof optionvalue
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 →
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:
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:
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
<style>
.my-input,
.my-list {
background: #fff;
border-radius: .25em;
border: 2px solid #090C33;
box-sizing: border-box;
color: #090C33;
font: inherit;
padding: .5em;
width: 13em;
transition: .2s; /* Animate */
}
.my-input {
background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='m3 8 9 9 9-9'/%3E%3C/svg%3E") center right/2rem 1rem no-repeat;
}
.my-input[aria-expanded="true"] {
background-color: #f9ffd7;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.my-input:focus-visible,
.my-list u-option:focus {
box-shadow: 0 0 0 1px #fff,0 0 0 3px #6325e7,0 0 0 4px #fff;
outline: none;
}
.my-list {
border-top-left-radius: 0;
border-top-right-radius: 0;
box-shadow: 0 .3em 1em #090C3333;
display: block; /* Overwrites hidden attribute */
margin-top: -2px;
position: absolute;
}
.my-list[hidden] {
opacity: 0;
translate: 0 -.5em;
visibility: hidden;
}
.my-list u-option {
border-radius: .1em;
padding: .5em;
transition: .2s;
}
.my-list u-option:focus {
background-color: #EBF0FA;
}
.my-list u-option[selected] {
font-weight: bold;
text-decoration: underline;
}
</style>
<label for="my-styling-input">
Choose flavor of ice cream
<input type="text" id="my-styling-input" class="my-input" list="my-styling" />
</label>
<u-datalist class="my-list" id="my-styling" data-sr-singular="%d flavor" data-sr-plural="%d flavours">
<u-option>Coconut</u-option>
<u-option>Strawberries</u-option>
<u-option>Chocolate</u-option>
<u-option>Vanilla</u-option>
</u-datalist>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus tristique tellus massa, eget sollicitudin arcu luctus vel. Cras non purus accumsan, ultricies mi ut, blandit magna.
</p>
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.
<label for="my-popover-input"> Choose flavor of ice cream <input type="text" id="my-popover-input" list="my-popover" /> </label> <u-datalist popover id="my-popover" data-sr-singular="%d flavor" data-sr-plural="%d flavours"> <u-option>Coconut</u-option> <u-option>Strawberries</u-option> <u-option>Chocolate</u-option> <u-option>Vanilla</u-option> </u-datalist>
Example: Dynamic
<label for="my-dynamic-input">Choose your email</label>
<input type="text" id="my-dynamic-input" list="my-dynamic-list" placeholder="Type email..." />
<u-datalist id="my-dynamic-list" data-nofilter data-sr-singular="%d suggestion" data-sr-plural="%d suggestions">
</u-datalist>
<script type="module">
const input = document.getElementById('my-dynamic-input');
input.addEventListener('input', (event) => {
const value = input.value.split("@")[0].trim();
const values = [
`${value}@live.com`,
`${value}@icloud.com`,
`${value}@hotmail.com`,
`${value}@gmail.com`,
];
input.list.textContent = '';
if (value)
input.list?.append(...values.map((text) =>
Object.assign(document.createElement("u-option"), { text }))
);
});
</script>
Server side rendering
You can server side render <u-datalist> by using Declarative Shadow DOM.
Styling and markup needed is exported as UHTMLDataListShadowRoot. Example:
import { UHTMLDataListShadowRoot } from '@u-elements/u-datalist';
const renderToStaticMarkup = (options: string) =>
`<u-datalist>
${UHTMLDataListShadowRoot}
${options}
</u-datalist>`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
- DOM interface: HTMLDatalistElement
- HTML Standard: The <datalist> element
- DOM interface: HTMLOptionElement
- HTML Standard: The <option> element
Changelog
- 1.0.14: Enable declarative shadow root support and export
UHTMLDataListShadowRootfor easier server side rendering - 1.0.13: Prevent opening if
inputmode="none" - 1.0.12: Prevent ESC from closing
dialogwhen datalist is open - 1.0.11: Respects input
disabledandreadonlyand moves caret to end of text onarrow up - 1.0.10: Automatically update
popovertargetandaria-controlsof input whenidof 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",hiddenordisabledto 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
Androidcompatibility by not relying on focus events - 1.0.3: Improved
JSDOMcompatibility by not assigningviewinFocusEvent - 1.0.2: Fix error when consumer removes
<input>element duringinputevent - 1.0.1: Improved
popovercompatibility - 1.0.0:
- Removed support for
isDatalistClick,syncDatalistStateandgetDatalistValue - Added support for
data-nofilter - Fixed bug where VoiceOver + Safari announced incorrect amount of list items
- Improved support for changes in
disabled,hidden,labelandvalueattributes - Improved
popoversupport
- Removed support for