A form-associated listbox component for single or multiple selection. Supports keyboard navigation, form integration, and accessible selection patterns.
Basic Usage
Code
<m-listbox name="fruit">
<m-option value="apple">Apple</m-option>
<m-option value="pear">Pear</m-option>
<m-option value="orange">Orange</m-option>
<m-option value="banana">Banana</m-option>
</m-listbox> Form Participation
Code
<form id="fruit-form" class="form">
<label for="favorite-fruit">Favorite fruit</label>
<m-listbox id="favorite-fruit" name="favorite-fruit" required>
<m-option value="apple">Apple</m-option>
<m-option value="pear">Pear</m-option>
<m-option value="orange">Orange</m-option>
<m-option value="banana">Banana</m-option>
</m-listbox>
<div id="form-result" class="box" data-variant="surface" data-span="2">
<strong>Form Data:</strong>
<pre id="form-result-text" style="margin: 0.5rem 0 0 0;"></pre>
</div>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<script>
const form = document.getElementById('fruit-form');
const resultText = document.getElementById('form-result-text');
form.addEventListener('submit', (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(form));
resultText.textContent = JSON.stringify(data, null, 2);
});
</script> Selection Modes
Code
<div class="form">
<label for="single-select">Single select (default)</label>
<m-listbox id="single-select" name="single">
<m-option value="red">Red</m-option>
<m-option value="green">Green</m-option>
<m-option value="blue">Blue</m-option>
</m-listbox>
<label for="single-focus">Single focus</label>
<m-listbox id="single-focus" name="single-focus" mode="single-focus">
<m-option value="red">Red</m-option>
<m-option value="green">Green</m-option>
<m-option value="blue">Blue</m-option>
</m-listbox>
<label for="multi-select">Multiple select</label>
<m-listbox id="multi-select" name="multiple" mode="multiple">
<m-option value="red">Red</m-option>
<m-option value="green">Green</m-option>
<m-option value="blue">Blue</m-option>
<m-option value="yellow">Yellow</m-option>
</m-listbox>
</div> Pre Selected Values
Code
<div class="form">
<label for="selected-attr">Using selected attribute</label>
<m-listbox id="selected-attr" name="selected-attr">
<m-option value="apple">Apple</m-option>
<m-option value="pear" selected>Pear</m-option>
<m-option value="orange">Orange</m-option>
</m-listbox>
<label for="value-attr">Using value attribute</label>
<m-listbox id="value-attr" name="value-attr" value="orange">
<m-option value="apple">Apple</m-option>
<m-option value="pear">Pear</m-option>
<m-option value="orange">Orange</m-option>
</m-listbox>
</div> Disabled State
Code
<div class="form">
<label for="disabled-listbox">Disabled listbox</label>
<m-listbox id="disabled-listbox" name="disabled" disabled>
<m-option value="apple">Apple</m-option>
<m-option value="pear" selected>Pear</m-option>
<m-option value="orange">Orange</m-option>
</m-listbox>
<label for="disabled-options">Disabled options</label>
<m-listbox id="disabled-options" name="partial-disabled">
<m-option value="apple">Apple</m-option>
<m-option value="pear" disabled>Pear (disabled)</m-option>
<m-option value="orange">Orange</m-option>
</m-listbox>
</div> Validation
Code
<form id="validation-form" class="form">
<label for="required-listbox">Country (required)</label>
<m-listbox id="required-listbox" name="country" required>
<m-option value="us">United States</m-option>
<m-option value="uk">United Kingdom</m-option>
<m-option value="ca">Canada</m-option>
<m-option value="au">Australia</m-option>
</m-listbox>
<div id="validation-output" class="box" data-variant="surface" data-span="2">
<strong>Validation Status:</strong>
<pre id="validation-text" style="margin: 0.5rem 0 0 0;">Not yet validated</pre>
</div>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<script>
const validationForm = document.getElementById('validation-form');
const requiredListbox = document.getElementById('required-listbox');
const validationText = document.getElementById('validation-text');
requiredListbox.addEventListener('m-invalid', (e) => {
validationText.textContent = `Invalid: ${e.detail.validationMessage}`;
validationText.style.color = 'var(--color-destructive-fill-mid)';
});
requiredListbox.addEventListener('change', () => {
if (requiredListbox.value) {
validationText.textContent = `Valid: "${requiredListbox.value}" selected`;
validationText.style.color = 'var(--color-success-fill-mid)';
}
});
validationForm.addEventListener('submit', (e) => {
e.preventDefault();
validationText.textContent = 'Form submitted successfully!';
validationText.style.color = 'var(--color-success-fill-mid)';
});
</script> Events
Event Log:
Code
<div class="stack" data-gap="2">
<m-listbox id="event-listbox" name="event-example" mode="multiple">
<m-option value="apple">Apple</m-option>
<m-option value="pear">Pear</m-option>
<m-option value="orange">Orange</m-option>
<m-option value="banana">Banana</m-option>
</m-listbox>
<div id="event-log" class="box" data-variant="surface">
<strong>Event Log:</strong>
<ul id="event-log-list" style="margin: 0.5rem 0 0 0; padding-left: 1.5rem; max-height: 200px; overflow-y: auto;"></ul>
</div>
</div>
<script>
const eventListbox = document.getElementById('event-listbox');
const eventLogList = document.getElementById('event-log-list');
function logEvent(message, color = 'var(--color-neutral-fill-mid)') {
const li = document.createElement('li');
li.textContent = message;
li.style.color = color;
eventLogList.prepend(li);
if (eventLogList.children.length > 10) {
eventLogList.removeChild(eventLogList.lastChild);
}
}
eventListbox.addEventListener('m-listbox-change', (e) => {
logEvent(`m-listbox-change: [${e.detail.selected.join(', ') || 'none'}]`, 'var(--color-primary-fill-mid)');
});
eventListbox.addEventListener('m-listbox-select', (e) => {
logEvent(`m-listbox-select: "${e.detail.option.value}"`, 'var(--color-success-fill-mid)');
});
eventListbox.addEventListener('m-listbox-unselected', (e) => {
logEvent(`m-listbox-unselected: "${e.detail.option.value}"`, 'var(--color-destructive-fill-mid)');
});
eventListbox.addEventListener('m-listbox-focus-change', (e) => {
const value = e.detail.item?.value || 'none';
logEvent(`m-listbox-focus-change: "${value}"`, 'var(--color-neutral-fill-mid)');
});
</script> Focus Management
Focused: None
Selected: None
Selected: None
Code
<div class="stack" data-gap="2">
<m-listbox id="focus-demo" name="focus-demo">
<m-option value="item-1">Item 1</m-option>
<m-option value="item-2">Item 2</m-option>
<m-option value="item-3">Item 3</m-option>
<m-option value="item-4">Item 4</m-option>
</m-listbox>
<div class="box" data-variant="surface" style="padding: var(--size-2);">
<strong>Focused:</strong> <span id="focus-output">None</span><br>
<strong>Selected:</strong> <span id="select-output">None</span>
</div>
</div>
<script>
const focusDemo = document.getElementById('focus-demo');
const focusOutput = document.getElementById('focus-output');
const selectOutput = document.getElementById('select-output');
focusDemo.addEventListener('m-listbox-focus-change', (e) => {
focusOutput.textContent = e.detail.item?.value || 'None';
});
focusDemo.addEventListener('m-listbox-change', (e) => {
selectOutput.textContent = e.detail.selected.join(', ') || 'None';
});
</script> Keyboard Navigation
Code
<div class="form">
<label for="keyboard-demo">Try keyboard navigation</label>
<m-listbox id="keyboard-demo" name="keyboard-demo" mode="multiple">
<m-option value="option-1">Option 1</m-option>
<m-option value="option-2">Option 2</m-option>
<m-option value="option-3">Option 3</m-option>
<m-option value="option-4">Option 4</m-option>
<m-option value="option-5">Option 5</m-option>
<m-option value="option-6">Option 6</m-option>
</m-listbox>
</div> Accessibility
Code
<div class="form">
<label for="accessible-listbox">Accessible listbox with label</label>
<m-listbox
id="accessible-listbox"
name="accessible"
label="Choose your preferred contact method"
required
>
<m-option value="email">Email</m-option>
<m-option value="phone">Phone</m-option>
<m-option value="sms">SMS</m-option>
<m-option value="mail">Postal Mail</m-option>
</m-listbox>
</div> - mode string "single-select"
- Selection mode: "single-select" | "single-focus" | "multiple"
- skip string | undefined —
- options MOption[] —
- Array of all available option elements
- selectedOptions MOption[] —
- Array of all selected option elements
- selectedValues string[] —
- Array of all selected option values
- form HTMLFormElement | null —
- The associated form element (readonly)
- handleMouseOver — —
- handleMouseOut — —
- adoptedStyleSheets array [baseStyleSheet]
- value string | string[] | null —
- Current value (string in single-select, array in multiple-select, null when empty)
- disabled boolean —
- Whether the listbox is disabled
- required boolean —
- Whether selection is required
- readonly boolean —
- Whether the listbox is readonly
- name string —
- The form control name
- label string —
- The accessible label
- setFocus void
- Sets virtual focus to a specific option. Updates aria-activedescendant and triggers focus state on the option. Does nothing if option is null.
- focusFirst void
- Moves virtual focus to the first available option. Wraps to first if already at the end.
- focusLast void
- Moves virtual focus to the last available option. Wraps to last if already at the beginning.
- focusNext void
- Moves virtual focus to the next option in the list. Wraps to the first option if currently on the last.
- focusPrev void
- Moves virtual focus to the previous option in the list. Wraps to the last option if currently on the first.
- focusBlur void
- Removes virtual focus from all options. Clears the aria-activedescendant attribute.
- selectFocused void
- Selects the currently focused option. In single-select mode, deselects all other options. In multiple-select mode, toggles the focused option's selection.
- selectFirst void
- Selects the first available option. In single-select mode, deselects all other options.
- selectLast void
- Selects the last available option. In single-select mode, deselects all other options.
- selectNext void
- Selects the next option after the currently selected one. In single-select mode, deselects all other options. Wraps to first if currently on last.
- selectPrev void
- Selects the previous option before the currently selected one. In single-select mode, deselects all other options. Wraps to last if currently on first.
- formResetCallback void
- * ---------------------------- Form association -----------------------------
- formDisabledCallback void
- select void
- Selects or toggles selection of a list box option. In single-select mode, deselects all other options and selects the target option. In multiple-select mode, toggles the selection state of the target option. Dispatches m-listbox-select, m-listbox-unselected, and m-listbox-change events.
- change Event
- Standard change event (bubbles)
- m-listbox-select MListboxSelectEvent
- Fired when an option is selected. Detail: { option: MOption }
- m-listbox-unselected MListboxUnselectedEvent
- Fired when an option is unselected. Detail: { option: MOption }
- m-listbox-change MListboxChangeEvent
- Fired when the selection changes. Detail: { option: MOption | null, selected: string[] }
- m-listbox-focus-change MListboxFocusChangeEvent
- Fired when focus moves to a different option. Detail: { option: MOption | null }
- m-invalid MInvalidEvent
- Fired when validation fails (inherited). Detail: { validity, validationMessage, value }
- (default)
- The default slot accepts m-option elements