A progressively enhanced, lightweight, customizable, and accessible <select>
custom element with text input.
A single file, weighing only , and having no dependencies.
Based on Adam Silver's article Building an accessible auto-complete control, and some improvements of my own.
All you need to do is wrap your <select>
element inside a <better-select>
element, and the <select>
will be enhanced. If for some reason, your JavaScript fails to load (or until it loads), users will still be able to access a working <select>
menu. You don't need to change your forms input handling either.
<script src="./better-select.js" type="module"></script>
<better-select>
<select name="country" id="country">
<option value="">Select</option>
<option value="in">India</option>
<option value="au">Australia</option>
<option value="ja">Japan</option>
</select>
</better-select>
You can import better-select.js from CDNs like unpkg. The package is also available on npm.
<script type="module">
import BetterSelect from "./better-select.js";
customElements.define("my-better-select", BetterSelect);
</script>
The custom element can be styled using CSS custom properties. Following properties are presently available along with their default values:
better-select {
/* input box */
--input-color: #000;
--input-background: #fff;
--input-border-width: 2px;
--input-border-color: #718096;
/* focused/active input box */
--outline-width: 3px;
--outline-color: #ecc94b;
/* options list wrapper */
--menu-max-height: 16em;
--menu-background: var(--input-background);
/* options */
--item-padding: 0.5em;
--item-color: #000;
--item-background: #fff;
--item-color-active: #fff;
--item-background-active: #111;
/* dropdown arrow */
--caret-color: var(--input-color);
}
Method 1: Extend isMatch
method of BetterSelect
class:
import BetterSelect from "./better-select.js";
class MyBetterSelect extends BetterSelect {
constructor() {
super();
}
/**
* @param {HTMLOptionElement} option an option from select element
* @param {string} value the value user has typed
* @returns {boolean} whether this option be listed or not
*/
isMatch(option, value) {
return option.getAttribute("data-value").startsWith(value);
}
}
customElements.define("my-better-select", MyBetterSelect);
Method 2: Add a globally available function name as an attribute to the element
<better-select match="myMatcherFunction">
<select name="country" id="country">
<option value="in">India</option>
<!-- … -->
</select>
</better-select>
<script>
function myMatcherFunction(option, value) {
return option.value.startsWith(value);
}
</script>
- Reporting issues is welcome.
- Sending pull requests is more welcome.