<bmx-combobox>
A text field that filters a list as you type. The other half of roster item
5, and a separate element from bmx-select rather than a flag on it.
33 properties · 6 events · 9 methods · 13 parts
Example
Show markup
<div class="row">
<bmx-combobox id="ex-combo-country" label="Country" placeholder="Start typing…" clearable="true" description="Type to filter. Only something from the list can be left in the field."></bmx-combobox>
</div>
<div class="row">
<bmx-combobox id="ex-combo-tags" label="Labels" multiple="true" allow-custom="true" placeholder="Choose or invent…" description="allow-custom turns the same component into a tag input — press Enter on text that matches nothing."></bmx-combobox>
</div>
<script type="module">
await customElements.whenDefined('bmx-combobox');
document.getElementById('ex-combo-country').options = [
{ value: 'gb', label: 'United Kingdom', group: 'Europe' },
{ value: 'ie', label: 'Ireland', group: 'Europe' },
{ value: 'de', label: 'Germany', group: 'Europe' },
{ value: 'es', label: 'Spain', group: 'Europe' },
{ value: 'us', label: 'United States', group: 'Americas' },
{ value: 'ca', label: 'Canada', group: 'Americas' },
];
document.getElementById('ex-combo-tags').options = [
{ value: 'bug', label: 'Bug' },
{ value: 'chore', label: 'Chore' },
{ value: 'docs', label: 'Documentation' },
{ value: 'urgent', label: 'Urgent' },
];
</script>
WHY IT IS A SEPARATE ELEMENT
The two look like one component with a switch and are not. A select's trigger
is a button, so every printable key is typeahead; a combobox's is a text
field, so every printable key is text and Home and End belong to the caret
rather than to the list. A select announces aria-haspopup="listbox"; a
combobox announces aria-autocomplete. A searchable flag would silently
change an element's role and its keyboard between two renders, which is the
thing this library keeps declining to do - see src/core/choice.ts for the
same argument about two keyboard modules.
What they genuinely share is the list, and they share it as code:
../listbox-view.tsx, ../listbox-geometry.ts and ../listbox.css, so a
grouped, windowed list behaves identically in both.
THE SEARCHABLE SELECT
allow-custom is off by default, which makes this the control people
usually mean when they ask for a searchable select: the user may type to
find, but may only leave with something from the list. Turn it on and the
typed text becomes the value - a tag input, a free-text-with-suggestions
field. The distinction matters at exactly one moment, blur, and that is where
it is implemented.
FILTERING SOMEWHERE ELSE
bmxFilter fires on every edit, and server-filter turns the local filter
off. Without that flag a consumer answering bmxFilter from an API would
have their results filtered again by the same query on the way in - which
works for a substring search and quietly discards everything a fuzzy or
synonym search was for.
ACCESSIBILITY
The APG editable-combobox pattern: role="combobox" on the input itself with
aria-autocomplete="list", aria-expanded and aria-controls, and
aria-activedescendant moving the visual focus while the caret stays where
the user put it. Home and End are deliberately not bound to the list,
because in a text field they belong to the caret and taking them is the
quickest way to make a combobox feel broken to anyone who touch-types.
The result count is announced through a live region. A list that silently shrinks as you type tells a sighted user everything and a screen reader user nothing at all.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
allowCustom |
allow-custom |
boolean |
false |
Whether a value the user typed but did not choose is allowed. Off by default, which is the "searchable select": type to find, leave with something from the list. On, the typed text becomes the value - which is what makes this a tag input when combined with multiple. |
appearance |
appearance |
BmxComboboxAppearance |
'outline' |
Visual treatment. |
autoFocus |
auto-focus |
boolean |
false |
Focus the field once it has rendered. |
clearable |
clearable |
boolean |
false |
Show a clear button once something is chosen. |
description |
description |
string |
— | Help text below the field. |
disabled |
disabled |
boolean |
false |
Disable the field. |
emptyText |
empty-text |
string |
'No matches' |
What the list says when nothing matches. |
errorText |
error-text |
string |
— | An error supplied by the consumer - a server response, typically. |
fullWidth |
full-width |
boolean |
false |
Stretch to the width of the container. |
hideLabel |
hide-label |
boolean |
false |
Hide the label visually while keeping it as the accessible name. |
label |
label |
string |
— | The field's label. Required unless the label slot is used. |
loading |
loading |
boolean |
false |
Show a busy state while a consumer fetches results. |
loadingText |
loading-text |
string |
'Searching…' |
What the list says while loading is set. |
matcher |
property only | BmxOptionMatcher |
— | A consumer's own matcher, in place of the default. |
maxTags |
max-tags |
number |
3 |
How many tags to show before the rest collapse into a count. |
maxVisibleRows |
max-visible-rows |
number |
8 |
How many rows the open list shows before it scrolls. |
messages |
property only | BmxFieldMessages |
— | Replacements for the default wording, by reason. Accepts the JSON spelling of the object as well, for templates that can only write attributes. See src/core/markup.ts. |
multiple |
multiple |
boolean |
false |
Allow more than one choice, shown as tags. |
name |
name |
string |
— | The field's name in the form it belongs to. |
options |
property only | BmxListboxOption[] |
[] |
The options. A property, because it is an array. Accepts the JSON spelling of the list as well, because an attribute is the only channel some templates have. See src/core/markup.ts. |
placeholder |
placeholder |
string |
'Type to search…' |
Placeholder text. Never a substitute for a label. |
placement |
placement |
BmxPlacement |
'bottom-start' |
Which side the list opens on when there is room. |
readonly |
readonly |
boolean |
false |
Make the field read-only. It still submits and is still focusable. |
required |
required |
boolean |
false |
Require a choice before the form will submit. |
serverFilter |
server-filter |
boolean |
false |
Filter elsewhere. See the note on the class. |
shape |
shape |
BmxShape |
'rounded' |
Corner treatment. circle is not meaningful here and behaves as pill. |
showChevron |
show-chevron |
boolean |
true |
Show the disclosure arrow, which opens the unfiltered list. |
size |
size |
BmxSize |
'md' |
Size step. |
tone |
tone |
BmxTone |
'primary' |
Semantic colour role, used for the focus ring and the chosen option. |
validateOn |
validate-on |
BmxValidateOn |
'blur' |
When the field is willing to reveal a problem. |
value |
value |
string | null |
null |
The chosen value in single mode. Two-way. |
values |
property only | string[] |
[] |
The chosen values in multiple mode. Two-way. Accepts a comma-separated or JSON string as well, because an attribute is the only channel some templates have. See src/core/markup.ts. |
virtualThreshold |
virtual-threshold |
number |
100 |
The row count above which the list is windowed. |
Events
| Event | Detail | Description |
|---|---|---|
bmxBlur |
void |
Fired when the field loses focus. |
bmxChange |
BmxComboboxChangeDetail |
Fired when the selection changes. |
bmxFilter |
BmxComboboxFilterDetail |
Fired on every edit, with what the user has typed. |
bmxFocus |
void |
Fired when the field gains focus. |
bmxOpenChange |
boolean |
Fired when the list opens or closes. |
bmxValidityChange |
BmxComboboxValidityDetail |
Fired whenever the resolved validity changes. |
Methods
| Method | Signature | Description |
|---|---|---|
checkValidity |
checkValidity() => Promise<boolean> |
Validate now and return whether the field passed, without revealing anything. |
clear |
clear() => Promise<void> |
Empty the selection and the text. |
closeList |
closeList() => Promise<void> |
Close the list without committing. |
getQuery |
getQuery() => Promise<string> |
What the user has typed. |
getSelectedOptions |
getSelectedOptions() => Promise<BmxListboxOption[]> |
The options behind the current selection. |
openList |
openList() => Promise<void> |
Open the list, unfiltered. |
removeFocus |
removeFocus() => Promise<void> |
Remove focus from the field. |
reportValidity |
reportValidity() => Promise<boolean> |
Validate, reveal any problem, and focus the field if it has one. |
setFocus |
setFocus(options?: FocusOptions) => Promise<void> |
Focus the field. |
Slots
| Slot | Description |
|---|---|
(default) |
bmx-option elements, when options are written rather than passed. |
description |
Rich help text, in place of the description property. |
label |
Rich label content, in place of the label property. |
CSS shadow parts
| Part | Description |
|---|---|
chevron |
The disclosure arrow. |
clear |
The clear button. |
control |
The text input carrying role="combobox". |
description |
The help text. |
empty |
The message shown when nothing matches. |
error |
The error message. |
field |
The bordered box. |
group |
A group heading. |
label |
The label element. |
listbox |
The floating surface. |
option |
One option row. |
tag |
One chosen value in multiple mode. |
tag-remove |
A tag's remove button. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-combobox-background |
The field's background. Set by appearance. |
--bmx-combobox-border-color |
The field's border colour at rest. |
--bmx-combobox-border-width |
Border width of the field. |
--bmx-combobox-font-size |
The text's font size. |
--bmx-combobox-gap |
Space between the tags, the input and the chevron. |
--bmx-combobox-group-height |
A group heading's height. |
--bmx-combobox-height |
The field's height. |
--bmx-combobox-label-font-size |
The label's font size. |
--bmx-combobox-list-background |
The floating list's background. |
--bmx-combobox-list-shadow |
The list's shadow. |
--bmx-combobox-option-active-background |
The background of the option the keyboard is on. |
--bmx-combobox-option-height |
A row's height in the open list. |
--bmx-combobox-option-selected-background |
The chosen option's background. |
--bmx-combobox-padding-inline |
Horizontal padding inside the field. |
--bmx-combobox-placeholder-color |
Placeholder colour. Dimmer than a value, and still AA against the field. |
--bmx-combobox-radius |
Corner radius of the field and the list. |
--bmx-combobox-stack-gap |
Space between the label, the field and the supporting text. |
--bmx-combobox-support-font-size |
Font size of the description and error message. |