Select
<ahi-select> | AhiSelect
Select lets users choose one or more items from a dropdown menu. Consider select when you have 4 or more options, to avoid cluttering the interface.
Use the label attribute to give the select an accessible label. For labels that contain HTML,
use the label slot instead.
<ahi-select label="Select one" placeholder="Select">
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
This component works with standard <form> elements. Please refer to the section on
form controls to learn
more about form submission and client-side validation.
Examples
Help text
Add descriptive help text to a select with the help-text attribute. For help texts that contain
HTML, use the help-text slot instead.
<ahi-select
label="Reason for appointment"
placeholder="Select"
help-text="If you would like to request an appointment for a different reason, please call your provider for assistance."
>
<ahi-option value="1">Follow up visit</ahi-option>
<ahi-option value="2">Physical</ahi-option>
<ahi-option value="3">Sick</ahi-option>
</ahi-select>
Clearable
Use the clearable attribute to make the control clearable.
<ahi-select label="Select one" placeholder="Select" clearable>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
Disabled
Use the disabled attribute to disable a select.
<ahi-select label="Select one" placeholder="Select" disabled>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
Multiple
To allow multiple options to be selected, use the multiple attribute. It’s a good practice to
use clearable when this option is enabled. To set multiple values at once, set
value to a space-delimited list of values.
<ahi-select label="Multiple options" value="option-1 option-2 option-3" multiple clearable>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
<ahi-divider></ahi-divider>
<ahi-option value="option-4">Option 4</ahi-option>
<ahi-option value="option-5">Option 5</ahi-option>
<ahi-option value="option-6">Option 6</ahi-option>
</ahi-select>
Note that multi-select options may wrap, causing the control to expand vertically. You can use the
max-options-visible attribute to control the maximum number of selected options to show at
once.
Setting initial values
Use the value attribute to set the initial selection.
When using multiple, the value property is an array of the selected
options’ values, and option values may contain any characters — including spaces. Only the
value attribute is space-delimited (HTML attributes can’t express arrays), so options
whose values contain spaces can’t be preselected from markup; assign an array to the
value property instead.
<ahi-select label="Select one" value="option-1 option-2" multiple clearable>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
Grouping options
Use <ahi-divider> to group listbox items visually.
<ahi-select label="Grouped options" placeholder="Select one">
<ahi-menu-label>Group 1</ahi-menu-label>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
<ahi-divider></ahi-divider>
<ahi-menu-label>Group 2</ahi-menu-label>
<ahi-option value="option-4">Option 4</ahi-option>
<ahi-option value="option-5">Option 5</ahi-option>
<ahi-option value="option-6">Option 6</ahi-option>
</ahi-select>
Placement
The preferred placement of the select’s listbox can be set with the placement attribute. Note
that the actual position may vary to ensure the panel remains in the viewport. Valid placements are
top and bottom.
<ahi-select label="Opens upward" placement="top">
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
Prefix & suffix
Use the prefix and suffix slots to add presentational icons and text. Avoid
slotting in interactive elements, such as buttons, links, etc.
<ahi-select label="With prefix and suffix" placeholder="Small" clearable>
<ahi-icon name="home" slot="prefix"></ahi-icon>
<ahi-badge slot="suffix">New</ahi-badge>
<ahi-option value="option-1">Option 1</ahi-option>
<ahi-option value="option-2">Option 2</ahi-option>
<ahi-option value="option-3">Option 3</ahi-option>
</ahi-select>
Lazy loading options
Lazy loading options is very hard to get right. <ahi-select> largely follows how a native
<select> works.
Here are the following conditions:
-
If a
<ahi-select>is created without any options, but is given avalueattribute, itsvaluewill be"", and then when options are added, if any of the options have a value equal to the<ahi-select>value, the value of the<ahi-select>will equal that of the option.
EX: <ahi-select value="foo"> will have a value of "" until
<ahi-option value="foo">Foo</ahi-option> connects, at which point its value will
become "foo" when submitting.
-
If a
<ahi-select multiple>with an initial value has multiple values, but only some of the options are present, it will only respect the options that are present, and if a selected option is loaded in later, AND the value of the select has not changed via user interaction or direct property assignment, it will add the selected option to the form value and to the.valueof the select.
This can be hard to conceptualize, so here’s a fairly large example showing how lazy loaded options work
with <ahi-select> and <ahi-select multiple> when given initial value
attributes.
<form id="lazy-options-example">
<div>
<ahi-select name="select-1" value="foo" label="Single select (with existing options)">
<ahi-option value="bar">Bar</ahi-option>
<ahi-option value="baz">Baz</ahi-option>
</ahi-select>
<br>
<ahi-button type="button">Add "foo" option</ahi-button>
</div>
<br>
<div>
<ahi-select name="select-2" value="foo" label="Single select (with no existing options)">
</ahi-select>
<br>
<ahi-button type="button">Add "foo" option</ahi-button>
</div>
<br>
<div>
<ahi-select name="select-3" value="foo bar baz" multiple label="Multiple Select (with existing options)">
<ahi-option value="bar">Bar</ahi-option>
<ahi-option value="baz">Baz</ahi-option>
</ahi-select>
<br>
<ahi-button type="button">Add "foo" option</ahi-button>
</div>
<br>
<div>
<ahi-select name="select-4" value="foo" multiple label="Multiple Select (with no existing options)">
</ahi-select>
<br>
<ahi-button type="button">Add "foo" option</ahi-button>
</div>
<br><br>
<div style="display: flex; gap: 16px;">
<ahi-button type="reset">Reset</ahi-button>
<ahi-button type="submit" variant="brand">Show FormData</ahi-button>
</div>
<br>
<pre hidden><code id="lazy-options-example-form-data"></code></pre>
<br>
</form>
<script type="module">
function addFooOption(e) {
const addFooButton = e.target.closest("ahi-button[type='button']")
if (!addFooButton) {
return
}
const select = addFooButton.parentElement.querySelector("ahi-select")
if (select.querySelector("ahi-option[value='foo']")) {
// Foo already exists. no-op.
return
}
const option = document.createElement("ahi-option")
option.setAttribute("value", "foo")
option.innerText = "Foo"
select.append(option)
}
function handleLazySubmit (event) {
event.preventDefault()
const formData = new FormData(event.target)
const codeElement = document.querySelector("#lazy-options-example-form-data")
const obj = {}
for (const key of formData.keys()) {
const val = formData.getAll(key).length > 1 ? formData.getAll(key) : formData.get(key)
obj[key] = val
}
codeElement.textContent = JSON.stringify(obj, null, 2)
const preElement = codeElement.parentElement
preElement.removeAttribute("hidden")
}
const container = document.querySelector("#lazy-options-example")
container.addEventListener("click", addFooOption)
container.addEventListener("submit", handleLazySubmit)
</script>
Text assistance and mobile keyboards
The search field accepts the same assistance and keyboard attributes as <ahi-input>:
autocapitalize, autocomplete, autocorrect, spellcheck,
inputmode and enterkeyhint.
The first four default to off, which is what a typeahead wants — the field matches the literal string you type against its options, so a keyboard that rewrites or capitalizes your input makes it search for something you did not ask for. Turn any of them back on if your field is prose rather than an identifier.
<ahi-select label="Client" autocorrect="on" autocapitalize="sentences"></ahi-select>
Use inputmode to raise the right keyboard, and enterkeyhint to label its Enter
key:
<ahi-select label="Account number" inputmode="numeric" enterkeyhint="search"></ahi-select>
Bind autocorrect as a string, not a boolean. autocorrect is a
boolean IDL attribute whose content attribute is the string "on" or
"off". A framework that forces attributes through setAttribute — Vue does —
stringifies a bound false to "false", and the HTML spec says any value that is
not "off" means on. So :autocorrect="false" would switch
autocorrect on, the exact opposite of the intent.
AKU deliberately departs from the spec here: "false" and "0" are read as off,
alongside "off". So the mistake is harmless in AKU. It is still clearer to write
autocorrect="off", and other custom elements will not be so forgiving.
Importing
To import this component using a script tag:
<script type="module" src="/path/to/azalea-kit-ui/dist/components/select/select.js"></script>
To import this component using a bundler:
import '@azaleahealth/azalea-kit-ui/dist/components/select/select.js';
Slots
| Name | Description |
|---|---|
| (default) |
The listbox options. Must be <ahi-option> elements. You can use
<ahi-divider> to group items visually.
|
label
|
The select’s label. Alternatively, you can use the label attribute. |
prefix
|
Used to prepend an icon or similar element to the select. |
clear-icon
|
An icon to use in lieu of the default clear icon. |
expand-icon
|
An icon to use in lieu of the default expand icon. |
help-text
|
Text that describes how to use the select. Alternatively, you can use the
help-text attribute.
|
error
|
An error message shown once the control is invalid. Plain text can be set with the
error property instead; use this slot when the message needs markup.
|
Learn more about using slots.
Properties
| Name | Description | Type | Default | |
|---|---|---|---|---|
autocapitalize
|
Controls whether and how text entered into the search field is automatically capitalized. |
'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
|
'off'
|
|
autocomplete
|
Specifies what permission the browser has to autofill the search field. Defaults to
off, which is what a typeahead wants: the field matches against the option list, not a
saved value.
|
string
|
'off'
|
|
autocorrect
|
Indicates whether the browser’s autocorrect feature is on or off. As an attribute, use
"on" or "off"; as a property it is a boolean, matching the platform’s
autocorrect IDL attribute. Defaults to off. A typeahead matches the literal string
against its options, so a keyboard that rewrites what was typed makes the field search for something
the user did not ask for – which is how the identifier uqbdbecame uAndon
blur (CORE-631).
|
boolean
|
false
|
|
enterkeyhint
|
Used to customize the label or icon of the Enter key on virtual keyboards. |
'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
|
- | |
inputmode
|
Tells the browser what type of data will be entered into the search field, so it can raise the appropriate virtual keyboard on supportive devices. |
'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'
|
- | |
spellcheck
|
Enables spell checking on the search field. Off by default, for the same reason autocorrect is. |
boolean
|
false
|
|
defaultValue
value
|
The default value of the form control, set via the value attribute. Primarily used for
resetting the form control. When multiple is enabled the attribute form is a
space-delimited list, so attribute-authored values must not contain spaces (assign an array to the
value property for values that do).
|
string | string[]
|
''
|
|
placeholder
|
Placeholder text to show as a hint when the select is empty. |
string
|
''
|
|
multiple
|
Allows more than one option to be selected. |
boolean
|
false
|
|
maxOptionsVisible
max-options-visible
|
The maximum number of selected options to show when multiple is true. After the
maximum, ”+n” will be shown to indicate the number of additional items that are selected. Set to 0
to remove the limit.
|
number
|
3
|
|
disabled
|
Disables the select control. |
boolean
|
false
|
|
clearable
|
Adds a clear button when the select is not empty. |
boolean
|
false
|
|
open
|
Indicates whether or not the select is open. You can toggle this attribute to show and hide the
menu, or you can use the show() and hide() methods and this attribute will
reflect the select’s open state.
|
boolean
|
false
|
|
hoist
|
Deprecated. Enabled a fixed positioning strategy to keep the listbox from being
clipped by a container with overflow: auto|scroll. The listbox now renders in the
browser’s native top layer, which escapes clipping containers on its own, so this has no effect
except as a fallback on browsers without Popover API support. Remove it from your markup; no
replacement is needed.
|
boolean
|
false
|
|
label
|
The select’s label. If you need to display HTML, use the label slot instead. |
string
|
''
|
|
placement
|
The preferred placement of the select’s menu. Note that the actual placement may vary as needed to keep the listbox inside of the viewport. |
'top' | 'bottom'
|
'bottom'
|
|
helpText
help-text
|
The select’s help text. If you need to display HTML, use the help-text slot instead.
|
string
|
''
|
|
error
|
An error message to show on the control. Setting it makes the control genuinely invalid: it enters
the user-invalid state, matches :invalid, and blocks native form
submission — which is what a consumer binding a server-side rejection expects. Clearing it clears
that validity. Use the error slot instead when the message needs markup rather than
plain text.
|
string
|
''
|
|
required
|
The select’s required attribute. |
boolean
|
false
|
|
validity
|
Gets the validity state object | - | - | |
validationMessage
|
Gets the validation message | - | - | |
value
|
The current value of the control, submitted as a name/value pair with form data. When
multiple is enabled, the value is an array of the selected options’ values and option
values may contain any characters, including spaces — only the space-delimited
value attribute restricts them (see defaultValue). In single mode,
assigning an array keeps its first entry.
|
- | - | |
formId
form
|
By default, form controls are associated with the nearest containing
<form> element. This attribute allows you to place the form control outside of a
form and associate it with the form that has this id. The form must be in the same
document or shadow root for this to work.
|
string
|
''
|
|
form
|
The form the control is associated with, following the native form-control contract: reading returns
the associated <form> element (or null if there is none), assigning
takes a form id — assign an empty string to restore association with the nearest containing form.
|
HTMLFormElement | null
|
- | |
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about properties and attributes.
Events
| Name | Description | Event Detail |
|---|---|---|
ahi-change |
Emitted when the control’s value changes. | - |
ahi-clear |
Emitted when the control’s value is cleared. | - |
ahi-input |
Emitted when the control receives input. | - |
ahi-focus |
Emitted when the control gains focus. | - |
ahi-blur |
Emitted when the control loses focus. | - |
ahi-show |
Emitted when the select’s menu opens. | - |
ahi-after-show |
Emitted after the select’s menu opens and all animations are complete. | - |
ahi-hide |
Emitted when the select’s menu closes. | - |
ahi-after-hide |
Emitted after the select’s menu closes and all animations are complete. | - |
ahi-invalid |
Emitted when the form control has been checked for validity and its constraints aren’t satisfied. | - |
Learn more about events.
Methods
| Name | Description | Arguments |
|---|---|---|
show() |
Shows the listbox. | - |
hide() |
Hides the listbox. | - |
checkValidity() |
Checks for validity but does not show a validation message. Returns true when valid and
false when invalid.
|
- |
reportValidity() |
Checks for validity and shows the browser’s validation message if the control is invalid. | - |
setCustomValidity() |
Sets a custom validation message. Pass an empty string to restore validity. |
message: string
|
focus() |
Sets focus on the control. |
options: FocusOptions
|
blur() |
Removes focus from the control. | - |
resetValidity() |
Hides validity again, returning the control to its pristine state: it stops matching
:user-invalid and drops its error styling until the user next interacts with it. The
value and the validity are both untouched — an empty required control is still invalid and still
blocks submission. This only changes whether that invalidity is shown. Interaction state
otherwise has exactly one automatic exit, form.reset(). Call this when the application
replaces a value itself and the old error no longer describes anything the user did: loading a
draft, switching which record is being edited, or clearing a filter row.
|
- |
Learn more about methods.
CSS Parts
| Name | Description |
|---|---|
form-control |
The form control that wraps the label, input, and help text. |
form-control-label |
The label’s wrapper. |
form-control-input |
The select’s wrapper. |
form-control-help-text |
The help text’s wrapper. |
form-control-error |
The error’s wrapper. |
combobox |
The container the wraps the prefix, suffix, combobox, clear icon, and expand button. |
prefix |
The container that wraps the prefix slot. |
suffix |
The container that wraps the suffix slot. |
display-input |
The element that displays the selected option’s label, an <input> element. |
listbox |
The listbox container where options are slotted. |
tags |
The container that houses option tags when multiselect is used. |
tag |
The individual tags that represent each multiselect option. |
tag__base |
The tag’s base part. |
tag__content |
The tag’s content part. |
tag__remove-button |
The tag’s remove button. |
tag__remove-button__base |
The tag’s remove button base part. |
clear-button |
The clear button. |
expand-icon |
The container that wraps the expand icon. This control inherits
setCustomValidity(message), checkValidity() and
reportValidity() from the form-control base class. Because they are inherited rather
than declared here, they do not appear in this component’s generated type definitions – which is why
they are easy to conclude do not exist. They do, and they work.
|
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<ahi-icon><ahi-icon-button><ahi-popup><ahi-tag>