Now.js Framework Documentation
Text-based Elements
Text-based Elements
Documentation for TextElementFactory, PasswordElementFactory, and SearchElementFactory - text input components with autocomplete, validation, and specialized features.
📋 Table of Contents
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"}
]
}Hierarchical Search
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 + Skeyboard 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 inputEvents
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">Related Documentation
- Form Elements Overview
- ElementFactory - Base class
- FormManager - Form-level validation
- Number Elements - Number inputs
- Select Elements - Dropdown inputs