CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nouislider

Lightweight JavaScript range slider with no dependencies

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

event-handling.mddocs/

Event Handling

Comprehensive event system for responding to slider interactions and value changes with detailed callback information and namespacing support.

Capabilities

on()

Binds an event listener to the slider.

/**
 * Bind an event listener to the slider
 * @param eventName - Event name, optionally with namespace (e.g., 'update.myNamespace')
 * @param callback - Function to call when event fires
 */
slider.noUiSlider.on(eventName: string, callback: EventCallback): void;

type EventCallback = (
    values: string[],      // Formatted values for all handles
    handle: number,        // Index of the handle that triggered the event
    unencoded: number[],   // Raw numeric values for all handles
    tap?: boolean,         // True if triggered by tap/click
    positions?: number[],  // Handle positions as percentages (0-100)
    slider?: SliderAPI     // Reference to the slider API
) => void;

Usage Examples:

// Basic event binding
slider.noUiSlider.on('update', function(values, handle) {
    console.log('Handle', handle, 'updated to:', values[handle]);
});

// Access all callback parameters
slider.noUiSlider.on('slide', function(values, handle, unencoded, tap, positions, slider) {
    console.log('Values:', values);           // ["20.00", "80.00"] (formatted)
    console.log('Handle:', handle);           // 1 (active handle index)  
    console.log('Raw values:', unencoded);    // [20, 80] (numeric)
    console.log('Tap event:', tap);           // true/false
    console.log('Positions:', positions);     // [20, 80] (percentages)
    console.log('Slider API:', slider);       // Reference to slider
});

// Event with namespace
slider.noUiSlider.on('update.validation', function(values) {
    validateInput(values);
});

off()

Removes event listeners from the slider.

/**
 * Remove event listeners from the slider
 * @param eventName - Event name, namespace, or combination to remove
 */
slider.noUiSlider.off(eventName: string): void;

Usage Examples:

// Remove all listeners for an event
slider.noUiSlider.off('update');

// Remove listeners for specific namespace
slider.noUiSlider.off('.validation');

// Remove specific event with namespace
slider.noUiSlider.off('update.validation');

// Remove all namespaced events
slider.noUiSlider.off('.myNamespace');

Event Types

start

Fired when user interaction begins (mousedown, touchstart).

slider.noUiSlider.on('start', callback);

Usage:

slider.noUiSlider.on('start', function(values, handle, unencoded) {
    console.log('Started dragging handle', handle);
    document.body.style.cursor = 'grabbing';
});

slide

Fired continuously during user interaction (mousemove, touchmove).

slider.noUiSlider.on('slide', callback);

Usage:

slider.noUiSlider.on('slide', function(values, handle, unencoded) {
    // Real-time feedback during dragging
    document.getElementById('current-value').textContent = values[handle];
});

update

Fired when slider values change, from any source.

slider.noUiSlider.on('update', callback);

Usage:

slider.noUiSlider.on('update', function(values, handle, unencoded) {
    // Update form inputs, display values, etc.
    document.getElementById('value-' + handle).value = unencoded[handle];
});

change

Fired when user interaction ends and value has changed.

slider.noUiSlider.on('change', callback);

Usage:

slider.noUiSlider.on('change', function(values, handle, unencoded) {
    // Save to database, validate final value, etc.
    console.log('Final value changed to:', values[handle]);
    saveToDatabase(unencoded);
});

set

Fired when values are set programmatically via set() or setHandle().

slider.noUiSlider.on('set', callback);

Usage:

slider.noUiSlider.on('set', function(values, handle, unencoded) {
    console.log('Value programmatically set to:', values[handle]);
});

// This will trigger the 'set' event
slider.noUiSlider.set([30, 70]);

end

Fired when user interaction ends (mouseup, touchend).

slider.noUiSlider.on('end', callback);

Usage:

slider.noUiSlider.on('end', function(values, handle, unencoded) {
    console.log('Finished interacting with handle', handle);
    document.body.style.cursor = '';
});

hover

Fired when hovering over the slider (requires 'hover' behaviour).

slider.noUiSlider.on('hover', callback);

Usage:

// Enable hover behaviour
noUiSlider.create(element, {
    start: 50,
    range: { min: 0, max: 100 },
    behaviour: 'hover'
});

// Listen for hover events
slider.noUiSlider.on('hover', function(value) {
    // Note: hover callback receives single value, not array
    console.log('Hovering over value:', value);
    showTooltip(value);
});

Event Namespacing

Use namespaces to organize and manage event listeners:

// Add namespaced listeners
slider.noUiSlider.on('update.ui', updateUI);
slider.noUiSlider.on('update.validation', validateValues);
slider.noUiSlider.on('change.persistence', saveToDatabase);

// Remove specific namespace
slider.noUiSlider.off('.ui');          // Removes only UI-related listeners
slider.noUiSlider.off('.validation');  // Removes only validation listeners

// Remove specific event with namespace
slider.noUiSlider.off('update.ui');    // Removes only UI update listener

// Multiple namespaces
slider.noUiSlider.on('update.ui.form', function(values) {
    // Update form fields
});

Practical Usage Patterns

Form Integration

// Two-way binding with form inputs
const priceRange = document.getElementById('price-range');
const minInput = document.getElementById('min-price');
const maxInput = document.getElementById('max-price');

noUiSlider.create(priceRange, {
    start: [100, 500],
    connect: true,
    range: { min: 0, max: 1000 }
});

// Update inputs when slider changes
priceRange.noUiSlider.on('update', function(values, handle) {
    if (handle === 0) {
        minInput.value = Math.round(values[0]);
    } else {
        maxInput.value = Math.round(values[1]);
    }
});

// Update slider when inputs change
minInput.addEventListener('change', function() {
    priceRange.noUiSlider.set([this.value, null]);
});

maxInput.addEventListener('change', function() {
    priceRange.noUiSlider.set([null, this.value]);
});

Real-time Validation

slider.noUiSlider.on('update.validation', function(values, handle, unencoded) {
    const isValid = unencoded.every(value => value >= 10 && value <= 90);
    
    if (!isValid) {
        slider.target.classList.add('invalid');
        showError('Values must be between 10 and 90');
    } else {
        slider.target.classList.remove('invalid');
        hideError();
    }
});

Debounced API Calls

let debounceTimer;

slider.noUiSlider.on('slide', function(values) {
    // Clear previous timer
    clearTimeout(debounceTimer);
    
    // Set new timer for API call
    debounceTimer = setTimeout(function() {
        updateSearchResults(values);
    }, 300);
});

// Immediate update on final change
slider.noUiSlider.on('change', function(values) {
    clearTimeout(debounceTimer);
    updateSearchResults(values);
});

Multi-slider Coordination

// Coordinate multiple related sliders
const slider1 = document.getElementById('slider1');
const slider2 = document.getElementById('slider2');

// When slider1 changes, update slider2's range
slider1.noUiSlider.on('update.coordination', function(values) {
    const maxValue = parseFloat(values[0]);
    
    slider2.noUiSlider.updateOptions({
        range: {
            min: 0,
            max: maxValue
        }
    });
});

Custom Event Dispatching

// Dispatch custom DOM events
slider.noUiSlider.on('change', function(values, handle, unencoded) {
    const event = new CustomEvent('slider-changed', {
        detail: {
            values: values,
            unencoded: unencoded,
            handle: handle,
            slider: this
        }
    });
    
    document.dispatchEvent(event);
});

// Listen for custom events elsewhere
document.addEventListener('slider-changed', function(event) {
    console.log('Slider changed:', event.detail);
});

Event Callback Parameters

values: string[]

Formatted values for all handles using the format.to() function.

// With custom formatter
format: {
    to: value => '$' + Math.round(value)
}

// values array contains: ["$25", "$75"]

handle: number

Zero-based index of the handle that triggered the event.

slider.noUiSlider.on('update', function(values, handle) {
    if (handle === 0) {
        console.log('First handle changed');
    } else if (handle === 1) {
        console.log('Second handle changed');
    }
});

unencoded: number[]

Raw numeric values for all handles, not processed by formatters.

// Always contains actual numeric values
// unencoded: [25, 75] (regardless of formatting)

tap: boolean (optional)

True if the event was triggered by a tap/click action.

slider.noUiSlider.on('change', function(values, handle, unencoded, tap) {
    if (tap) {
        console.log('Value changed by clicking');
    } else {
        console.log('Value changed by dragging');
    }
});

positions: number[] (optional)

Handle positions as percentages (0-100).

slider.noUiSlider.on('update', function(values, handle, unencoded, tap, positions) {
    console.log('Handle positions:', positions); // [25, 75] (percentages)
});

slider: SliderAPI (optional)

Reference to the slider API object.

slider.noUiSlider.on('update', function(values, handle, unencoded, tap, positions, sliderAPI) {
    // Access other slider methods
    const allValues = sliderAPI.get();
    const options = sliderAPI.options;
});

Error Handling

Event handling is generally robust, but be aware of:

// Events fire even for invalid operations
slider.noUiSlider.on('update', function(values) {
    try {
        updateUI(values);
    } catch (error) {
        console.error('Error updating UI:', error);
    }
});

// Prevent infinite loops in cross-updates
let updating = false;
slider.noUiSlider.on('update', function(values) {
    if (updating) return;
    updating = true;
    
    // Update related elements
    updateRelatedSlider(values);
    
    updating = false;
});

Install with Tessl CLI

npx tessl i tessl/npm-nouislider

docs

configuration-updates.md

event-handling.md

index.md

slider-creation.md

ui-enhancements.md

value-management.md

tile.json