Now.js Framework Documentation

Now.js Framework Documentation

Select Elements

EN 28 Dec 2025 08:20

Select Elements

Documentation for SelectElementFactory and MultiSelectElementFactory - dropdown and multi-select components with type-to-filter, Ajax loading, and keyboard navigation.

📋 Table of Contents

  1. Overview
  2. SelectElementFactory
  3. MultiSelectElementFactory
  4. Events
  5. Best Practices

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>