Now.js Framework Documentation

Now.js Framework Documentation

Text-based Elements

EN 10 Dec 2025 07:01

Text-based Elements

Documentation for TextElementFactory, PasswordElementFactory, and SearchElementFactory - text input components with autocomplete, validation, and specialized features.

📋 Table of Contents

  1. Overview
  2. TextElementFactory
  3. PasswordElementFactory
  4. SearchElementFactory
  5. Events
  6. Best Practices

Overview

Text-based elements handle text input with features like autocomplete, validation, and formatting. They share the TextElementFactory base and register different data-element types.

Element data-element Description
TextElementFactory text Text input with autocomplete, hidden input support
PasswordElementFactory password Password with strength meter, toggle visibility
SearchElementFactory search Search input with debounce, results container

TextElementFactory

TextElementFactory manages text inputs with autocomplete and hidden input support. When autocomplete is enabled, it creates a hidden input to store the selected value (key) while displaying the text (label) to the user.

Basic Usage

<!-- Simple text input -->
<input type="text"
       data-element="text"
       name="username"
       placeholder="Enter username">

<!-- Text with validation -->
<input type="text"
       data-element="text"
       data-validate="email"
       name="email"
       placeholder="Enter email">

Autocomplete Feature

Static Source (Array)

<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-source='[{"value":"1","text":"Bangkok"},{"value":"2","text":"Chiang Mai"}]'
       name="city"
       placeholder="Select city">

Datalist Source

<input type="text"
       data-element="text"
       list="cities"
       name="city">
<datalist id="cities">
  <option value="1" label="Bangkok">
  <option value="2" label="Chiang Mai">
  <option value="3" label="Phuket">
</datalist>

💡 Note: When using datalist, the <datalist> element is automatically removed and converted to an autocomplete dropdown.

Ajax Source

<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-source="/api/cities"
       data-min-length="2"
       name="city"
       placeholder="Search city...">

Expected API Response:

{
  "success": true,
  "data": [
    {"value": "1", "text": "Bangkok"},
    {"value": "2", "text": "Chiang Mai"}
  ]
}

For address fields (province → amphur → district → zipcode):

<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-search-api="/api/address/search"
       data-search-field="district"
       name="district"
       placeholder="Search district...">

When user selects an item, all related address fields are populated automatically.

Hidden Input Support

When autocomplete is enabled, TextElementFactory creates a hidden input to store the key value:

<!-- Before enhancement -->
<input type="text" name="city" data-element="text" data-autocomplete="true" ...>

<!-- After enhancement -->
<input type="text" name="city_text" ...>  <!-- Shows display text -->
<input type="hidden" name="city">          <!-- Stores key value -->

HTML Attributes

Attribute Type Default Description
data-element string - Set to text
data-autocomplete boolean false Enable autocomplete
data-source string/JSON - Autocomplete source (URL, array, or global variable name)
data-min-length number 2 Minimum characters before search
data-max-results number 10 Maximum results to display
data-delay number 300 Debounce delay (ms)
data-hierarchy string - Hierarchical level (province/amphur/district)
data-dependent string - ID of parent field for dependent dropdowns
data-callback string - Custom callback function name for rendering items
data-search-api string - API endpoint for hierarchical search
data-search-field string - Field name for hierarchical search
data-validate string - Validation rules (email, url, alpha, etc.)
data-formatter string - Formatter function name

Built-in Validators

Validator Pattern Description
email ^[^\s@]+@[^\s@]+\.[^\s@]+$ Valid email format
url HTTP/HTTPS URL pattern Valid URL
number ^-?\d*\.?\d+$ Numeric value
integer ^-?\d+$ Whole number
alpha ^[a-zA-Z]+$ Letters only
alphanumeric ^[a-zA-Z0-9]+$ Letters and numbers
usernameOrEmail Combined pattern Username or email

JavaScript API

// Get instance
const cityInput = document.querySelector('[name="city"]');
const instance = ElementManager.getInstance(cityInput);

// Access values
console.log(instance.element.value);      // Display text
console.log(instance.hiddenInput.value);  // Key value
console.log(instance.selectedValue);      // Currently selected key

// Programmatically update autocomplete options
instance.populate([
  {value: '1', text: 'Option 1'},
  {value: '2', text: 'Option 2'}
]);

// Show/hide dropdown
instance.show();
instance.hide();

Custom Render Callback

// Define callback function
window.renderCityItem = function({key, value, search, level}) {
  const div = document.createElement('div');
  div.className = 'city-item';
  div.innerHTML = `
    <strong>${value}</strong>
    <span class="code">${key}</span>
  `;
  return div;
};
<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-source="/api/cities"
       data-callback="renderCityItem"
       name="city">

PasswordElementFactory

PasswordElementFactory extends TextElementFactory with password-specific features: strength meter, toggle visibility, criteria list, and match validation.

Basic Usage

<!-- Password with toggle visibility -->
<input type="password"
       data-element="password"
       name="password"
       minlength="8"
       required>

<!-- Password with strength meter -->
<input type="password"
       data-element="password"
       data-password-strength="true"
       name="password"
       required>

Password Strength Meter

<input type="password"
       data-element="password"
       data-password-strength="true"
       id="password"
       name="password">
<span id="result_password" class="comment">At least 8 characters</span>
<!-- Strength bar will be inserted after comment -->

CSS Classes on Strength Bar:

  • .weak - Red (< 40% criteria met)
  • .medium - Yellow (40-80% criteria met)
  • .strong - Green (> 80% criteria met)

Password Criteria List

<input type="password"
       data-element="password"
       data-password-criteria-list="true"
       name="password">

Default Criteria:

  • ✅ At least 8 characters
  • ✅ At least one uppercase letter
  • ✅ At least one lowercase letter
  • ⬜ At least one number (disabled by default)
  • ⬜ At least one special character (disabled by default)

Password Match Validation

<!-- Main password field -->
<input type="password"
       data-element="password"
       id="password"
       name="password"
       required>

<!-- Confirm password field -->
<input type="password"
       data-element="password"
       data-target-password="password"
       name="password_confirm"
       required>

The confirm field will show valid/invalid state based on whether passwords match.

HTML Attributes

Attribute Type Default Description
data-element string - Set to password
data-password-strength boolean false Show strength meter
data-password-criteria-list boolean false Show criteria checklist
data-target-password string - ID of main password field (for confirm field)
minlength number 8 Minimum password length
maxlength number 50 Maximum password length

Password Toggle Visibility

Password toggle is enabled by default. Users can:

  • Click the eye icon to show/hide password
  • Press Alt + S keyboard shortcut

CSS Classes:

  • .icon-published0 - Password hidden (eye icon)
  • .icon-published1 - Password visible (eye-slash icon)

JavaScript API

const instance = ElementManager.getInstance(passwordInput);

// Check if passwords match
const confirmInput = document.getElementById('password_confirm');
const matches = instance.checkMatch(confirmInput);

// Check specific criterion
instance.passesCriterion('minLength', 'myPassword123'); // true/false
instance.passesCriterion('uppercase', 'myPassword123'); // true
instance.passesCriterion('numbers', 'myPassword123');   // true

// Get all failed criteria
const failed = instance.getFailedCriteria('weak');
// ['At least 8 characters', 'At least one uppercase letter']

// Update UI manually
instance.updateStrengthBar();
instance.updateCriteriaList();
instance.updateMatchStatus();

Customizing Criteria

// Override default criteria in create/enhance
PasswordElementFactory.enhance(element, {
  passwordCriteria: {
    minLength: {enabled: true, value: 12, label: 'At least {value} characters'},
    uppercase: {enabled: true, label: 'At least one uppercase letter'},
    lowercase: {enabled: true, label: 'At least one lowercase letter'},
    numbers: {enabled: true, label: 'At least one number'},
    special: {enabled: true, label: 'At least one special character (!@#$%^&*)'}
  }
});

SearchElementFactory

SearchElementFactory extends TextElementFactory with search-specific features: debounced input, clear button, results container, and FormManager integration.

Basic Usage

<!-- Simple search input -->
<input type="search"
       data-element="search"
       name="search"
       placeholder="Search...">

<!-- Search with onSearch callback -->
<input type="search"
       data-element="search"
       id="product-search"
       name="search"
       placeholder="Search products...">

<script>
const searchInput = document.getElementById('product-search');
searchInput.addEventListener('input', async (e) => {
  // Handle search with debounce built-in
});
</script>

Search with Results

// Create search with custom onSearch handler
SearchElementFactory.enhance(searchInput, {
  onSearch: async (query) => {
    const response = await fetch(`/api/products?q=${query}`);
    const data = await response.json();
    return data.results; // [{value, text}, ...]
  }
});

HTML Attributes

Attribute Type Default Description
data-element string - Set to search
data-search-delay number 300 Debounce delay (ms)
data-min-length number 2 Minimum characters before search
data-max-length number 100 Maximum input length
data-max-results number 10 Maximum results to show
data-highlight-matches boolean true Highlight matching text

Configuration Options

SearchElementFactory.config = {
  type: 'search',
  inputMode: 'search',
  searchDelay: 300,
  minLength: 2,
  maxLength: 100,
  placeholder: 'Search',
  wrapperClass: 'search',
  inputClass: 'search-input',
  clearClass: 'search-clear',
  loadingClass: 'search-loading',
  resultsClass: 'search-results',
  activeClass: 'active',
  maxResults: 10,
  highlightMatches: true,
  formIntegration: true,
  keyboard: {
    selectKeys: ['Enter', ' '],
    closeKeys: ['Escape'],
    navigateUpKeys: ['ArrowUp'],
    navigateDownKeys: ['ArrowDown']
  }
};

Clear Button

A clear button is automatically added and shown when the input has a value:

.search-clear {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  cursor: pointer;
}

Loading State

.search-loading {
  position: relative;
}

.search-loading::after {
  content: '';
  position: absolute;
  right: 40px;
  top: 50%;
  width: 16px;
  height: 16px;
  border: 2px solid #ccc;
  border-top-color: #333;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

JavaScript API

const instance = ElementManager.getInstance(searchInput);

// Show results programmatically
instance.createSuggestionList([
  {value: '1', text: 'Result 1'},
  {value: '2', text: 'Result 2'}
]);

// Access clear button
searchInput.clearButton.click(); // Clear input

Events

All text-based elements emit these events:

Common Events

Event When Emitted Detail
element:change Value changed {value, valid}
element:valid Validation passed {value}
element:invalid Validation failed {value, error}
input User types Native event
change Value confirmed Native event
focus Element focused Native event
blur Element blurred Native event

TextElementFactory Events

element.addEventListener('change', (e) => {
  const instance = ElementManager.getInstance(e.target);
  console.log('Display value:', e.target.value);
  console.log('Selected key:', instance.hiddenInput?.value);
});

PasswordElementFactory Events

// Listen to password changes with strength info
passwordInput.addEventListener('input', (e) => {
  const instance = ElementManager.getInstance(e.target);
  const failed = instance.getFailedCriteria(e.target.value);
  console.log('Failed criteria:', failed);
});

SearchElementFactory Events

// Now.js event system
Now.on('search:performed', ({elementId, query}) => {
  console.log(`Search performed on ${elementId}: ${query}`);
});

Best Practices

1. ✅ Use Hidden Input for Key-Value Pairs

<!-- ✅ Good: Autocomplete stores key in hidden input -->
<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-source="/api/cities"
       name="city">

<!-- Form submits: city=123 (hidden input value, not display text) -->

2. ✅ Set Appropriate Min Length for Autocomplete

<!-- ✅ Good: Wait for at least 2 characters -->
<input type="text"
       data-element="text"
       data-autocomplete="true"
       data-source="/api/products"
       data-min-length="2">

<!-- ❌ Bad: Triggers on every keystroke -->
<input type="text"
       data-autocomplete="true"
       data-source="/api/products"
       data-min-length="0">

3. ✅ Use Password Strength for Registration

<!-- ✅ Good: Show strength meter during registration -->
<form data-form="register">
  <input type="password"
         data-element="password"
         data-password-strength="true"
         data-password-criteria-list="true"
         name="password">
</form>

4. ✅ Match Passwords with data-target-password

<!-- ✅ Good: Use built-in match validation -->
<input type="password" id="password" name="password">
<input type="password"
       data-target-password="password"
       name="password_confirm">

<!-- ❌ Bad: Custom JavaScript for matching -->
<script>
  confirmInput.addEventListener('input', () => {
    if (passwordInput.value !== confirmInput.value) { ... }
  });
</script>

5. ✅ Debounce Search Input

<!-- ✅ Good: Built-in debounce with SearchElementFactory -->
<input type="search"
       data-element="search"
       data-search-delay="300">

<!-- ❌ Bad: No debounce on every keystroke -->
<input type="text" oninput="search(this.value)">

6. ✅ Provide Clear Error Messages

<input type="text"
       data-element="text"
       data-validate="email"
       data-error-email="Please enter a valid email address like example@domain.com"
       name="email">