Now.js Framework Documentation
Select Elements
Select Elements
Documentation for SelectElementFactory and MultiSelectElementFactory - dropdown and multi-select components with type-to-filter, Ajax loading, and keyboard navigation.
📋 Table of Contents
Overview
Select elements enhance native <select> elements with additional features while maintaining form compatibility.
| Element | data-element | Description |
|---|---|---|
| SelectElementFactory | select |
Enhanced select with type-to-filter |
| MultiSelectElementFactory | select-multiple |
Custom multi-select with checkbox UI |
Key Features
- ✅ Type-to-Filter: Jump to options by typing
- ✅ Ajax Loading: Load options from API
- ✅ OptGroups: Support grouped options
- ✅ Keyboard Navigation: Full keyboard support
- ✅ i18n Support: Auto-translate option labels
- ✅ Custom UI: MultiSelect with checkbox list
SelectElementFactory
SelectElementFactory enhances native <select> elements with type-to-filter, dynamic options loading, and improved accessibility.
Basic Usage
<!-- Simple select -->
<select data-element="select" name="country">
<option value="">Select country</option>
<option value="TH">Thailand</option>
<option value="US">United States</option>
<option value="JP">Japan</option>
</select>
<!-- Select with placeholder -->
<select data-element="select"
data-placeholder="Choose an option"
name="category">
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>HTML Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
data-element |
string | - | Set to select |
data-placeholder |
string | - | Placeholder text (first disabled option) |
data-type-to-filter |
boolean | true |
Enable type-to-filter |
data-search-method |
string | 'prefix' |
Search method: prefix, contains, fuzzy |
data-search-reset-delay |
number | 1000 |
Reset search term after (ms) |
data-show-search-status |
boolean | false |
Show search status indicator |
data-use-opt-groups |
boolean | false |
Enable option groups |
data-allow-placeholder-selection |
boolean | false |
Allow selecting placeholder |
data-url |
string | - | URL to load options from |
data-value |
string | - | Initial value after options load |
data-options-key |
string | - | Key for options from parent data |
multiple |
boolean | false |
Enable multiple selection |
size |
number | 1 |
Number of visible options |
Configuration Object
SelectElementFactory.config = {
multiple: false,
size: 1,
typeToFilter: true,
searchMethod: 'prefix', // 'prefix', 'contains', 'fuzzy'
searchResetDelay: 1000, // ms
showSearchStatus: false,
useOptGroups: false,
allowPlaceholderSelection: false,
optimizeRendering: true,
ariaLabelledBy: null,
emptyMessage: 'No options available',
customTemplate: null,
validationMessages: {
required: 'Please select an option'
}
};Type-to-Filter
When focused on a select, users can type to jump to matching options:
Search Methods:
| Method | Description | Example |
|---|---|---|
prefix |
Match from start | "Th" → "Thailand" |
contains |
Match anywhere | "land" → "Thailand" |
fuzzy |
Match characters in order | "tld" → "Thailand" |
<!-- Contains search -->
<select data-element="select"
data-search-method="contains"
name="country">
<!-- "land" will match Thailand -->
</select>Loading Options from URL
<select data-element="select"
data-url="/api/countries"
data-value="TH"
name="country">
<option value="">Select country</option>
</select>Expected API Response:
{
"success": true,
"data": [
{"value": "TH", "text": "Thailand"},
{"value": "US", "text": "United States"}
]
}Loading Options from Parent Data
When used in modals or forms with parent data:
<select data-element="select"
data-options-key="countries"
name="country">
<option value="">Select country</option>
</select>// Populate from options object
SelectElementFactory.populateFromOptions(selectElement, {
countries: [
{value: 'TH', text: 'Thailand'},
{value: 'US', text: 'United States'}
]
}, 'countries');
// Or populate all selects in a container
SelectElementFactory.populateFromOptionsInContainer(formElement, optionsData);Option Groups
<select data-element="select"
data-use-opt-groups="true"
name="region">
<option value="">Select region</option>
</select>instance.updateOptions([
{
label: 'Asia',
options: [
{value: 'TH', text: 'Thailand'},
{value: 'JP', text: 'Japan'}
]
},
{
label: 'Europe',
options: [
{value: 'DE', text: 'Germany'},
{value: 'FR', text: 'France'}
]
}
]);JavaScript API
const instance = ElementManager.getInstance(selectElement);
// Get current value
console.log(instance.value); // 'TH'
console.log(instance.selectedOption); // {value: 'TH', text: 'Thailand', index: 1}
// Set value
instance.value = 'US';
// Get all options
console.log(instance.options);
// [{value: '', text: 'Select...', disabled: true}, {value: 'TH', ...}]
// Update options dynamically
instance.updateOptions([
{value: '1', text: 'Option 1'},
{value: '2', text: 'Option 2', selected: true},
{value: '3', text: 'Option 3', disabled: true}
]);
// Load options from URL
await instance.loadOptions('/api/cities', {country: 'TH'});
// Clear options (keep placeholder)
instance.clearOptions();
// Select by value
instance.selectByValue('TH');
// Select by index
instance.selectByIndex(2);
// Update specific option
instance.updateOptionAt(1, {
text: 'Updated Text',
disabled: true
});Property Handlers
SelectElementFactory.propertyHandlers = {
value: {
get(element) { /* Returns string or array for multiple */ },
set(instance, newValue) { /* Sets selection */ }
},
options: {
get(element) { /* Returns array of option objects */ },
set(instance, newValue) { /* Updates options */ }
},
selectedOption: {
get(element) { /* Returns selected option details */ },
set(instance, newValue) { /* Sets selection by option object */ }
}
};MultiSelectElementFactory
MultiSelectElementFactory transforms native <select multiple> into a custom UI with a trigger button and dropdown panel with checkboxes.
Basic Usage
<select data-element="select-multiple"
multiple
name="categories[]">
<option value="1">Electronics</option>
<option value="2">Clothing</option>
<option value="3">Books</option>
<option value="4">Sports</option>
</select>Rendered UI:
- Original select is hidden
- Trigger button shows selected items
- Dropdown panel with checkbox/check icons
HTML Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
data-element |
string | - | Set to select-multiple |
data-placeholder |
string | - | Placeholder when nothing selected |
data-max-display-items |
number | 2 |
Max items to show before "+n items" |
multiple |
boolean | required | Must be set |
required |
boolean | false |
Require at least one selection |
Configuration Object
MultiSelectElementFactory.config = {
maxDisplayItems: 2,
emptyMessage: 'No options available',
validationMessages: {
required: 'Please select at least one option'
}
};Display Format
Based on maxDisplayItems setting:
| Selected Count | maxDisplayItems | Display |
|---|---|---|
| 0 | - | "Select categories" (placeholder) |
| 1 | 2 | "Electronics" |
| 2 | 2 | "Electronics, Clothing" |
| 3 | 2 | "Electronics, Clothing +1 items" |
| 5 | 2 | "Electronics, Clothing +3 items" |
Keyboard Navigation
| Key | Action |
|---|---|
Enter / Space |
Open/close dropdown |
↓ |
Move to next option |
↑ |
Move to previous option |
Space |
Toggle selection on highlighted item |
Escape / Tab |
Close dropdown |
JavaScript API
const instance = ElementManager.getInstance(selectElement);
// Get selected values
console.log(instance.getValue()); // ['1', '3']
console.log(instance.value); // ['1', '3']
// Get selected options with details
console.log(instance.selectedOptions);
// [{value: '1', text: 'Electronics'}, {value: '3', text: 'Books'}]
// Set values
instance.setValue(['1', '2', '4']);
// Clear selection
instance.clear();
// Update options
instance.updateOptions([
{value: '1', text: 'New Option 1'},
{value: '2', text: 'New Option 2'}
]);Styling
/* Trigger button */
.dropdown-button {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
background: var(--color-background);
cursor: pointer;
}
.dropdown-button.disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Selected items display */
.dropdown-display {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.dropdown-display.placeholder {
color: var(--color-text-muted);
}
/* Arrow icon */
.dropdown-arrow {
margin-left: 8px;
}
/* Dropdown items with check icons */
.autocomplete-list li .icon-check,
.autocomplete-list li .icon-uncheck {
margin-right: 8px;
}Loading Options from Parent Data
<select data-element="select-multiple"
data-options-key="permissions"
multiple
name="permissions[]">
</select>// Populate from options object
MultiSelectElementFactory.populateFromOptions(selectElement, {
permissions: [
{value: 'read', text: 'Read'},
{value: 'write', text: 'Write'},
{value: 'delete', text: 'Delete'}
]
}, 'permissions');
// Or populate all multi-selects in a container
MultiSelectElementFactory.populateFromOptionsInContainer(formElement, optionsData);Events
SelectElementFactory Events
| Event | When Emitted | Detail |
|---|---|---|
change |
Selection changed | Native event |
optionschanged |
Options updated | {options} |
locale:changed |
Language changed | Translations updated |
selectElement.addEventListener('change', (e) => {
const instance = ElementManager.getInstance(e.target);
console.log('Selected:', instance.selectedOption);
});
selectElement.addEventListener('optionschanged', (e) => {
console.log('New options:', e.detail.options);
});MultiSelectElementFactory Events
| Event | When Emitted | Detail |
|---|---|---|
change |
Selection changed | Native event on original select |
selectElement.addEventListener('change', (e) => {
const instance = ElementManager.getInstance(e.target);
console.log('Selected values:', instance.getValue());
});Best Practices
1. ✅ Always Include Placeholder
<!-- ✅ Good: Clear indication to select -->
<select data-element="select" name="country">
<option value="">Select country</option>
<option value="TH">Thailand</option>
</select>
<!-- ❌ Bad: No indication what to do -->
<select data-element="select" name="country">
<option value="TH">Thailand</option>
</select>2. ✅ Use data-options-key for Modal Forms
<!-- ✅ Good: Options loaded from modal data -->
<form data-form="user-edit">
<select data-element="select"
data-options-key="roles"
name="role_id">
</select>
</form>
<!-- Modal response provides options -->
<script>
// Modal response: {options: {roles: [{value: 1, text: 'Admin'}, ...]}}
</script>3. ✅ Use Contains Search for Long Lists
<!-- ✅ Good: Users can search anywhere -->
<select data-element="select"
data-search-method="contains"
name="country">
<!-- 200+ countries -->
</select>
<!-- ❌ Less ideal: Only prefix search -->
<select data-element="select"
data-search-method="prefix"
name="country">
</select>4. ✅ Set Initial Value with data-value
<!-- ✅ Good: Value set after options load -->
<select data-element="select"
data-url="/api/countries"
data-value="TH"
name="country">
</select>
<!-- ❌ Bad: Value may be lost during options load -->
<select data-element="select"
data-url="/api/countries"
value="TH"
name="country">
</select>5. ✅ Use Array Notation for MultiSelect Names
<!-- ✅ Good: Server receives array -->
<select data-element="select-multiple"
multiple
name="categories[]">
</select>
<!-- Form submission: categories[]=1&categories[]=3 -->6. ✅ Validate Required Selections
<!-- ✅ Good: Require at least one selection -->
<select data-element="select-multiple"
multiple
required
name="permissions[]">
</select>7. ✅ Use OptGroups for Categorized Options
<!-- ✅ Good: Grouped for clarity -->
<select data-element="select" data-use-opt-groups="true">
<optgroup label="Asia">
<option value="TH">Thailand</option>
</optgroup>
<optgroup label="Europe">
<option value="DE">Germany</option>
</optgroup>
</select>Related Documentation
- Form Elements Overview
- ElementFactory - Base class
- Text Elements - Text inputs with autocomplete
- FormManager - Form-level management
- ModalManager - Modal forms with options