Now.js Framework Documentation

Now.js Framework Documentation

Special Elements

EN 10 Dec 2025 07:07

Special Elements

Documentation for specialized form elements: ColorElementFactory, RangeElementFactory, TagsElementFactory, MaskElementFactory, and TextareaElementFactory.

📋 Table of Contents

  1. Overview
  2. ColorElementFactory
  3. RangeElementFactory
  4. TagsElementFactory
  5. MaskElementFactory
  6. TextareaElementFactory
  7. Best Practices

Overview

Special elements provide enhanced UI for specific input types that need more than basic text or number handling.

Element data-element Description
ColorElementFactory color Color picker with palette
RangeElementFactory range Single/dual range slider
TagsElementFactory tags Tags/chips input with autocomplete
MaskElementFactory mask Input masking (phone, date, etc.)
TextareaElementFactory textarea Auto-resize textarea

ColorElementFactory

ColorElementFactory creates a custom color picker with predefined palette and hex input.

Basic Usage

<input type="color"
       data-element="color"
       name="theme_color"
       value="#007bff">

<!-- Or with text input -->
<input type="text"
       data-element="color"
       name="bg_color"
       value="#ffffff">

HTML Attributes

Attribute Type Default Description
data-element string - Set to color
value string #000000 Initial color (hex)
disabled boolean false Disable picker
readonly boolean false Read-only mode

Default Color Palette

const COLOR_PALETTE = [
  '#FF5722', '#F44336', '#E91E63', '#9C27B0',
  '#673AB7', '#3F51B5', '#2196F3', '#03A9F4',
  '#00BCD4', '#009688', '#4CAF50', '#8BC34A',
  '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800',
  // ... and more standard colors
  '#FFFFFF', '#000000'
];

JavaScript API

const instance = ElementManager.getInstance(colorInput);

// Get current color
const color = instance.getColor();  // '#007bff'

// Set color
instance.setColor('#ff5722');

// Open/close picker
instance.open();
instance.close();
instance.toggle();

// Disable/enable
instance.setDisabled(true);
instance.setReadonly(true);

Features

  • Color Palette: Click predefined colors
  • Hex Input: Type custom hex values
  • Preview: Color preview in button
  • Copy/Paste: Ctrl+C to copy, Ctrl+V to paste colors
  • Contrast Text: Button text auto-adjusts for visibility

Events

colorInput.addEventListener('change', (e) => {
  console.log('Selected color:', e.target.value);
});

RangeElementFactory

RangeElementFactory creates enhanced range sliders with better UX than native range inputs. Supports single and dual range modes.

Basic Usage

<!-- Single range slider -->
<input type="range"
       data-element="range"
       min="0"
       max="100"
       value="50"
       name="volume">

<!-- Dual range slider (min-max) -->
<input type="range"
       data-element="range"
       min="0"
       max="1000"
       data-value="200-800"
       data-dual="true"
       name="price_range">

HTML Attributes

Attribute Type Default Description
data-element string - Set to range
min number 0 Minimum value
max number 100 Maximum value
step number 1 Step increment
value string - Single value or "min-max" for dual
data-dual boolean false Enable dual handles
data-show-value boolean true Show value label
data-format string - Value format (e.g., {value}%)
disabled boolean false Disable slider
readonly boolean false Read-only mode

Single Range

<input type="range"
       data-element="range"
       min="0"
       max="100"
       step="5"
       value="75"
       data-format="{value}%"
       name="opacity">

Dual Range (Min-Max)

<input type="range"
       data-element="range"
       min="0"
       max="10000"
       step="100"
       data-value="2000-8000"
       data-dual="true"
       data-format="฿{value}"
       name="budget">

JavaScript API

const instance = ElementManager.getInstance(rangeInput);

// Get value
const value = instance.getValue();
// Single: 75
// Dual: {min: 2000, max: 8000}

// Set value
instance.setValue(80);           // Single
instance.setValue({min: 1000, max: 5000});  // Dual

// Update constraints
instance.setMin(10);
instance.setMax(200);
instance.setStep(10);

// Enable/disable
instance.setDisabled(true);
instance.setReadonly(true);

Keyboard Navigation

Key Action
/ Decrease by step
/ Increase by step
Home Go to min
End Go to max
Page Up Increase by 10 steps
Page Down Decrease by 10 steps

Events

// Input event (while dragging)
rangeInput.addEventListener('input', (e) => {
  console.log('Current value:', e.detail.value);
});

// Change event (after release)
rangeInput.addEventListener('change', (e) => {
  console.log('Final value:', e.detail.value);
});

TagsElementFactory

TagsElementFactory transforms text input into a tags/chips input with visual tags, keyboard navigation, and optional autocomplete.

Basic Usage

<!-- Simple tags input -->
<input type="text"
       data-element="tags"
       name="keywords"
       placeholder="Add tags...">

<!-- Tags with autocomplete -->
<input type="text"
       data-element="tags"
       data-source="/api/tags"
       name="categories">

<!-- Tags with datalist -->
<input type="text"
       data-element="tags"
       list="available-tags"
       name="skills">
<datalist id="available-tags">
  <option value="1" label="JavaScript">
  <option value="2" label="Python">
  <option value="3" label="React">
</datalist>

HTML Attributes

Attribute Type Default Description
data-element string - Set to tags
data-source string/array - Autocomplete source
data-separator string , Separator for pasted values
data-max-tags number null Maximum tags allowed
data-duplicate boolean false Allow duplicate tags
data-placeholder string 'Add tags...' Input placeholder
data-min-length number 2 Min chars for autocomplete
data-options-key string - Key for parent options data
list string - Datalist ID for options

Configuration

TagsElementFactory.config = {
  type: 'text',
  placeholder: 'Add tags...',
  separator: ',',
  maxTags: null,
  allowDuplicate: false,
  minLength: 2,
  maxResults: 10,
  delay: 300
};

Keyboard Interaction

Key Action
Enter Add current text as tag
Backspace Remove last tag (when input empty)
/ Navigate autocomplete
Escape Close autocomplete

JavaScript API

const instance = ElementManager.getInstance(tagsInput);

// Get all tags
const tags = instance.getTags();
// [{key: '1', value: 'JavaScript'}, {key: '2', value: 'Python'}]

// Add tag
instance.addTag('3', 'React');

// Remove tag
instance.removeTag('2');

// Set all tags
instance.setTags([
  {key: '1', value: 'JavaScript'},
  {key: '4', value: 'Vue'}
]);

// Clear all
instance.clear();

// Update autocomplete options
instance.updateOptions([
  {value: '1', text: 'JavaScript'},
  {value: '2', text: 'Python'}
]);

Form Submission

Tags are submitted as hidden inputs:

<!-- Original input -->
<input data-element="tags" name="skills">

<!-- Generated hidden inputs -->
<input type="hidden" name="skills[]" value="1">
<input type="hidden" name="skills[]" value="2">
<input type="hidden" name="skills[]" value="3">

Events

tagsInput.addEventListener('change', (e) => {
  const instance = ElementManager.getInstance(e.target);
  console.log('Tags:', instance.getTags());
});

tagsInput.addEventListener('tags:add', (e) => {
  console.log('Added:', e.detail.tag);
});

tagsInput.addEventListener('tags:remove', (e) => {
  console.log('Removed:', e.detail.tag);
});

MaskElementFactory

MaskElementFactory applies input masks for structured data like phone numbers, dates, credit cards, etc.

Basic Usage

<!-- Phone number -->
<input type="tel"
       data-element="mask"
       data-mask="(###) ###-####"
       name="phone">

<!-- Thai phone -->
<input type="tel"
       data-element="tel"
       name="mobile">

<!-- Credit card -->
<input type="text"
       data-element="mask"
       data-mask="#### #### #### ####"
       name="card">

<!-- Date -->
<input type="text"
       data-element="mask"
       data-mask="##/##/####"
       name="date">

HTML Attributes

Attribute Type Default Description
data-element string - mask or tel
data-mask string - Mask pattern
data-placeholder-char string _ Character for unfilled positions
data-mask-on-init boolean true Apply mask on init
data-mask-on-change boolean true Apply mask on change
data-mask-on-blur boolean true Apply mask on blur
data-unmask-on-submit boolean false Remove mask on form submit

Mask Pattern Characters

Character Accepts
# Digit (0-9)
A Letter (A-Z, a-z)
* Alphanumeric
Other Literal (shown as-is)

Common Patterns

Use Case Pattern Example
US Phone (###) ###-#### (123) 456-7890
Thai Phone ###-###-#### 081-234-5678
Date ##/##/#### 31/12/2025
Time ##:## 14:30
Credit Card #### #### #### #### 1234 5678 9012 3456
SSN ###-##-#### 123-45-6789
Zip+4 #####-#### 12345-6789

TelElementFactory

Specialized mask for Thai phone numbers:

<input type="tel"
       data-element="tel"
       name="phone">
<!-- Auto applies: ###-###-#### -->

JavaScript API

const instance = ElementManager.getInstance(maskedInput);

// Get unmasked value
const raw = instance.getUnmaskedValue();  // '0812345678'

// Get masked value
const masked = instance.getValue();       // '081-234-5678'

// Set value (auto-masked)
instance.setValue('0898765432');
// Shows: 089-876-5432

Events

maskedInput.addEventListener('change', (e) => {
  const instance = ElementManager.getInstance(e.target);
  console.log('Masked:', e.target.value);
  console.log('Raw:', instance.getUnmaskedValue());
});

TextareaElementFactory

TextareaElementFactory enhances textarea with auto-resize functionality.

Basic Usage

<textarea data-element="textarea"
          name="description"
          rows="3"
          placeholder="Enter description..."></textarea>

HTML Attributes

Attribute Type Default Description
data-element string - Set to textarea
data-auto-resize boolean true Enable auto-resize
data-min-rows number 3 Minimum rows
data-max-rows number 10 Maximum rows before scroll
maxlength number - Character limit
data-show-counter boolean false Show character counter

Auto-Resize Behavior

The textarea automatically adjusts height based on content:

/* Textarea expands with content */
[data-element="textarea"] {
  min-height: calc(3 * 1.5em);  /* 3 rows minimum */
  max-height: calc(10 * 1.5em); /* 10 rows maximum */
  overflow-y: auto;             /* Scroll when max reached */
  resize: vertical;             /* Allow manual resize */
}

Character Counter

<textarea data-element="textarea"
          data-show-counter="true"
          maxlength="500"
          name="bio"></textarea>
<!-- Shows: 0/500 characters -->

JavaScript API

const instance = ElementManager.getInstance(textareaEl);

// Force resize recalculation
instance.resize();

// Get current height
const height = instance.getHeight();

Best Practices

1. ✅ Use Appropriate Element for Data Type

<!-- ✅ Colors -->
<input data-element="color" name="theme">

<!-- ✅ Price ranges -->
<input data-element="range" data-dual="true" name="budget">

<!-- ✅ Multiple categories -->
<input data-element="tags" name="categories">

<!-- ✅ Structured input -->
<input data-element="mask" data-mask="##/##/####" name="date">

2. ✅ Set Sensible Limits

<!-- ✅ Good: Limit tags -->
<input data-element="tags" data-max-tags="5" name="keywords">

<!-- ✅ Good: Limit textarea -->
<textarea data-element="textarea" maxlength="1000" data-max-rows="15"></textarea>

3. ✅ Provide Autocomplete for Tags

<!-- ✅ Good: User can discover existing tags -->
<input data-element="tags"
       data-source="/api/tags"
       name="skills">

<!-- ❌ Bad: User must guess tag names -->
<input data-element="tags" name="skills">

4. ✅ Use tel Type for Phone Numbers

<!-- ✅ Good: Mobile keyboard shows numbers -->
<input type="tel"
       data-element="tel"
       name="phone">

<!-- ❌ Less ideal: Text keyboard on mobile -->
<input type="text"
       data-element="mask"
       data-mask="###-###-####"
       name="phone">

5. ✅ Consider Unmask on Submit

<!-- ✅ Good: Server receives clean number -->
<input data-element="mask"
       data-mask="(###) ###-####"
       data-unmask-on-submit="true"
       name="phone">
<!-- Form submits: 1234567890 instead of (123) 456-7890 -->

6. ✅ Use Dual Range for Filters

<!-- ✅ Good: Price filter with min-max -->
<input data-element="range"
       data-dual="true"
       min="0"
       max="10000"
       step="100"
       data-format="฿{value}"
       name="price">