Select (Editor's Draft)
Overview
The <select>
is a control that provides a list of options for the user to select
from.
Use Cases
The <select>
control is primarily leveraged to select an option for within a form. For example
when your buying a shirt you may be provided with a <select>
that has options for sizes that you
then select the appropriate one for you.
Prior Art/Examples
<select> Properties
Property Name | Type | Default Value | Description | |
---|---|---|---|---|
autocomplete | string | off | Allows the developer to provide a hint on how to search the content within the <option> (s) | |
autofocus | bool | false | If set to true the input will have focus set on page load | |
disabled | bool | inherited from containing element | If set to true the user will not be able to interact with the control | |
form | string | the form that contains the <select> | Represents the form owner, via the form attribute being the same as the id of the <form> | |
labels | NodeList | A NodeList containing the <label> elements associated with the <select> element | ||
length | int | 0 | The number of <option> elements in the <select> element | |
multiple | bool | false | If set to true this will allow more than one <option> | |
name | string | null | Represents the name of the control | |
open | bool | false | A boolean that indicates whether the <select> is in the open state | |
options | HTMLOptionsCollection | Returns a HTMLOptionsCollection of the <option> elements contained by the <select> element | ||
required | bool | false | A value must be provided for the control if set to true | |
selectedIndex | int | -1 | The index of the first or last selected <option> element, depending on the value of multiple | |
selectedOptions | HTMLCollection | An HTMLCollection of the selected <option> elements | ||
size | int | 0 | If the <select> is shown with a scrollbar, this represents how many <option> s are visible. | |
type | string | "select-one" | Returns either "select-multiple" or "select-one" | |
validationMessage | string | "" | Represents the message that describes the validation constraints that the control does not satisfy | |
validity | ValidityState | Represents the current validity state of the control | ||
value | string | "" | Returns the value property of the first selected <option> if there is one, otherwise an empty string | |
willValidate | bool | true | A boolean that indicates whether the button is a candidate for constraint validation. It is false if any conditions bar it from constraint validation. |
<select> Methods
Name | Description |
---|---|
add | Adds an <option> to the <select> 's collection of <option> elements |
checkValidity | Checks whether the element violates any of its validity constraints. If so, fires invalid and returns false |
item | Returns the <option> at the specified index in the <select> 's <option> collection |
namedItem | Returns the <option> in the <select> 's <option> collection with the specified name |
remove | Removes the <option> at the specified index in the <select> 's <option> collection |
reportValidity | If the <select> violates any of its validity constraints, shows a visual indicator to the user, fires invalid , and returns false |
setCustomValidity | Sets the custom validity message of the <select> to the specified string, or clears any custom validity error if passed an empty string |
<optgroup> Properties
Property Name | Type | Default Value | Description |
---|---|---|---|
disabled | bool | inherited from containing element | If set to true the user will not be able to interact with any of the <options> (s) in the group |
label | string | The name of the group of <option> s. Required if element is used. |
<option> Properties
Property Name | Type | Default Value | Description |
---|---|---|---|
defaultSelected | bool | false | The initial value of the selected attribute |
disabled | bool | inherited from containing element | If set to true the user will not be able to interact with the <option> |
form | HTMLFormElement | Returns the form element that the <option> is associated with, the association is inherited from the <select> | |
index | long | The position of the <option> in the list of options it belongs to, in tree order | |
label | string | Reflects the value of the value attribute if it exists; else reflects Node.textContent | |
selected | bool | false | If set to true then the <option> is selected |
text | string | Contains the text content of the element | |
value | string | Reflects the value of the value attribute if it exists; else reflects Node.textContent |
Anatomy
Diagram
Structure
<select>
- The root element that contains the button and listbox [required]<button>
- The button element that contains the selected value and triggers the visibility of the listbox [required]<listbox>
- The wrapper that contains the<option>
(s) and<optgroup>
(s) [required]<optgroup>
- Groups<options>
together with a label [optional]<option>
- Can have one or more and represents the potential values that can be chosen by the user [required]
Content not allowed within the anatomy
The following interactive elements are NOT permitted within a <select>
or its
children:
- button (outside of the pre-defined one)
- datalist
- input (all types)
- meter
- progress
- select
- textarea
- anchor
- iframe
- object
Should <a> and <output> be included?
Should this apply to the entire anatomy or solely the <option> and <optgroup> element
Events
select
Event | Behavior | Impacts |
---|---|---|
change | Updates the textContent property of the selected-value slot with the value property text content | textContent prop |
part button
Event | Behavior | Impacts |
---|---|---|
click | Toggles the open state of the <select> | open state |
click | Toggles aria-expanded attribute of the button | aria-expanded attr |
keydown(space) | Toggles the open state of the <select> | open state |
keydown(space) | Toggles aria-expanded attribute of the button | aria-expanded attr |
keydown(enter) | Toggles the open state of the <select> | open state |
keydown(enter) | Toggles aria-expanded of the button part | aria-expanded attr |
part listbox
Event | Behavior | Impacts |
---|---|---|
keydown(down key) | Moves focus to the next <option> in the listbox | focus |
keydown(up key) | Moves focus to the previous <option> in the listbox | focus |
keydown(enter) | Changes the selected state of the current <option> and updates the <select> value property | selected prop value prop |
keydown(space) | Changes the selected state of the current <option> and updates the <select> value property | selected prop value prop |
keydown(enter) | If the <select> does not have the multiple attribute then toggle the state of open of the <select> | open state |
keydown(enter) | If the <select> does not have the multiple attribute then toggle the aria-expanded attribute of the button | aria-expanded attr |
keydown(space) | If the <select> does not have the multiple attribute then toggle the state of open of the <select> | open state |
keydown(space) | If the <select> does not have the multiple attribute then toggle the aria-expanded attribute of the button | aria-expanded attr |
keydown(escape) | Toggles the open state of the <select> | open state |
keydown(any single character that no other listbox event listens to) | Run the typeahead steps given <select> and event.key | focus |
part option
Event | Behavior | Impacts |
---|---|---|
click | Changes the selected state of the current <option> and updates the <select> value property | selected prop value prop |
click | If the <select> does not have the multiple attribute then toggle the state of open of the <select> | open state |
click | If the <select> does not have the multiple attribute then toggle the aria-expanded attribute of the button | aria-expanded attr |
Behavior
States
open
This state is applied to the <select>
when the listbox is visible to the user.
required
An <option>
from the <select>
must be selected when the required
attribute is set to true
valid
The <select>
meets all its validation constraints,
and is therefore considered to be valid.
invalid
The <select>
does not meet its validation constraints,
and is therefore considered to be invalid.
Interaction & transition of states
Default State
- The
selected-value
part should show the currently selected<option>
or the first<option>
. - The
listbox
part is not visible to the user. - The state of the
<select>
is set toopen
when the user invokes thebutton
by:
If an author wants a <select> to be open by default, they can set the <select>'s `open` property to true in the JavaScript responsible for initializing the component, or in HTML set the <select>'s `open` boolean content attribute, which reflects the `open` property.
Open State
- The currently selected
<option>
, or the first if one isn't selected, should be visible within the listbox. This may require moving the list to accomodate listbox positioning and available space. - The user can dismiss the listbox and remove the state of
open
from the<select>
in either of the following manners:- Triggering any of the listbox's light dismiss behaviors
- Selecting one, or more (if
multiple
is true), options by clicking or hitting theenter
orspace
key
Light dismiss
The listbox part has "light dismiss", behavior, defined as being dismissed (removing
the open
state of the <select>
, in this case) by either of the following things:
- The user presses the escape key.
- A focus change occurs (because of either user interaction or script), where the focus
target is outside of the subtree of the listbox. This includes the case where the user
invokes a non-focusable element, which causes focus to switch to the
<body>
.- There is one exception to this: if a user invokes a non-focusable element
in the subtree of the listbox, focus still moves to the
<body>
, but "light dismiss" does not occur.
- There is one exception to this: if a user invokes a non-focusable element
in the subtree of the listbox, focus still moves to the
Typeahead
The typeahead steps given a <select>
and a typed character
are as follows:
- Let
start new search
be false. - If longer than the typeahead search timeout
has elapsed since the previous invocation of the typeahead steps
for
listbox
, then setstart new search
to true and setbuffer
to an empty string. - Let
buffer
be<select>
'stypeahead buffer
- Append
typed character
tobuffer
. - Let
options
be the<select>
's list of options. - If
start new search
is true, then letstarting option
be the option after the currently focused option (this is the first option if the currently focused option is the last option). Otherwise letstarting option
be the currently focused option.The point is that if we're in the middle of a search, we should keep trying to match on the currently selected <option>. If this is a new search, we should start searching at the <option> after the currently selected <option>. If the user wanted the current <option>, then presumably they wouldn't still be typing search text.
- Let
newly focused option
be the result of finding the first matching option givenbuffer
,options
, andstarting option
. - If
newly focused option
is not null, move focus tonewly focused option
.
To find the first matching option given a character buffer buffer
and a list of options options
, and a starting option
, run these steps:
- For each
option
inoptions
, starting withstarting option
, proceeding until the last option, then starting from the first option and proceeding to the option beforestarting option
:- Let
match text
beoption.label
with whitespace at the beginning and end removed (whitespace characters between non-whitespace characters are not removed). - If
buffer
is a case-insensitive prefix ofmatch text
, then returnoption
.
- Let
- Return null.
The typeahead buffer
of a <select>
A buffer containing the characters typed by the user for typeahead. Initially an empty string.
Typeahead search timeout
This is the maximum time that may elapse between keystrokes without the typeahead search being reset. The value is left to implementations to determine. Typically it will be somewhere around 0.5-1s.
Listbox positioning
When the <select>
is in its open
state the listbox
should
- Be positioned allow for the greatest visibility of the
listbox
as possible. - Let currentBox equal the
button
'sgetBoundingClientRect
- Let viewportHeight equal document.innerWidth
- Let availableTop equal
viewportHeight - currentBox.top
- Let availableBottom equal
viewportHeight - currentBox.bottom
- If availableTop is greater than availableBottom:
- Position the
listbox
's border-box bottom edge with thebutton
's border-box top edge
- Position the
- else
- Position the
listbox
's border-box top edge with thebutton
's border-box bottom edge
- Position the
Some frameworks allow a forced directionality with the default being an auto positioning as described above.
Use with Assistive Technology
Implements the combobox role per the HTML AAM spec.
Security
Avoiding UI spoofing
If a listbox is rendered outside of the viewport, then it is highly recommended to limit control over the possible adjustments that an author can make. This is due to the developer being able to overlay their content with the application's UI, of which the user trusts and can result in clickjacking due to UI spoofing. If the application enables the author to have complete control over its appearance, the implementor MUST render the listbox within the viewport.
This also MUST apply to embedded content, such as an <iframe>
as it may try to mimick the appearance of the
site it is being embedded on.
Open Questions
- Provide arbitrary HTML into
<option>
elements- What is the value of an option with complex content? [Issue 69]
- How does focus work with focusable children within the control?
- How does form validation work?
Resources
- MDN Reference on Current <Select>
- MDN Reference of HTMLSelectElement
- WHATWG Living Standard on <Select>
Acknowledgements
Some method and attribute definitions were sourced from the MDN definitions