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

ui-enhancements.mddocs/

UI Enhancements

Additional UI features including tooltips, scale markers (pips), styling customization, and advanced interaction capabilities.

Capabilities

Pips (Scale Markers)

Scale markers that provide visual reference points along the slider track.

pips()

Adds or updates scale markers on the slider.

/**
 * Add or update scale markers (pips) on the slider
 * @param config - Pips configuration object
 * @returns HTML element containing the pips
 */
slider.noUiSlider.pips(config: PipsConfig): HTMLElement;

interface PipsConfig {
    mode: 'range' | 'steps' | 'positions' | 'count' | 'values';
    values?: number[];
    stepped?: boolean;
    density?: number;
    filter?: (value: number, type: number) => number;
    format?: Formatter;
}

removePips()

Removes all scale markers from the slider.

/**
 * Remove all scale markers from the slider
 */
slider.noUiSlider.removePips(): void;

Usage Examples:

// Basic pips using range mode
slider.noUiSlider.pips({
    mode: 'range',
    density: 3
});

// Count mode - specific number of markers
slider.noUiSlider.pips({
    mode: 'count',
    values: 5,  // 5 evenly spaced markers
    density: 2
});

// Positions mode - markers at specific percentages
slider.noUiSlider.pips({
    mode: 'positions',
    values: [0, 25, 50, 75, 100],
    density: 4
});

// Values mode - markers at specific values
slider.noUiSlider.pips({
    mode: 'values',
    values: [10, 25, 50, 75, 90],
    density: 3
});

// Steps mode - markers at step intervals
slider.noUiSlider.pips({
    mode: 'steps',
    density: 2,
    stepped: true
});

// Remove pips
slider.noUiSlider.removePips();

Pips Configuration Options

mode

Determines how pip positions are calculated.

  • 'range': Distribute pips evenly across the range
  • 'steps': Place pips at step intervals
  • 'positions': Place pips at specific percentage positions
  • 'count': Place a specific number of evenly distributed pips
  • 'values': Place pips at specific values

values

Array of values or positions (usage depends on mode).

// For 'positions' mode: percentage positions
values: [0, 25, 50, 75, 100]

// For 'values' mode: actual slider values  
values: [100, 500, 1000, 2000]

// For 'count' mode: number of pips
values: 6  // or as single number

density

Controls spacing and visibility of pips.

// Higher density = more pips shown
density: 1  // Show every pip
density: 2  // Show every other pip
density: 4  // Show every fourth pip

stepped

Whether to align pips to step values (for 'range' and 'steps' modes).

stepped: true   // Align to steps
stepped: false  // Use exact positions

filter

Function to customize which pips are displayed.

filter?: (value: number, type: number) => number;

// Type values:
// -1: No pip
//  0: Normal pip
//  1: Large pip  
//  2: Sub pip
// Custom filter example
slider.noUiSlider.pips({
    mode: 'range',
    density: 2,
    filter: function(value, type) {
        // Hide pips for odd values
        if (value % 2 === 1) {
            return -1;  // No pip
        }
        
        // Large pips for multiples of 10
        if (value % 10 === 0) {
            return 1;   // Large pip
        }
        
        return 0;  // Normal pip
    }
});

format

Custom formatting for pip labels.

slider.noUiSlider.pips({
    mode: 'count',
    values: 5,
    format: {
        to: function(value) {
            return '$' + Math.round(value);
        }
    }
});

Tooltips

Dynamic value display attached to slider handles.

Tooltip Configuration

Set during slider creation or via updateOptions.

tooltips?: boolean | Formatter | (boolean | Formatter)[];

removeTooltips()

Removes all tooltips from the slider.

/**
 * Remove all tooltips from the slider
 */
slider.noUiSlider.removeTooltips(): void;

getTooltips()

Returns array of tooltip elements.

/**
 * Get array of tooltip DOM elements
 * @returns Array of tooltip elements (null for handles without tooltips)
 */
slider.noUiSlider.getTooltips(): HTMLElement[];

Usage Examples:

// Enable default tooltips
noUiSlider.create(element, {
    start: [20, 80],
    range: { min: 0, max: 100 },
    tooltips: true
});

// Custom tooltip formatting
noUiSlider.create(element, {
    start: [1000, 3000],
    range: { min: 0, max: 5000 },
    tooltips: {
        to: function(value) {
            return '$' + Math.round(value).toLocaleString();
        }
    }
});

// Per-handle tooltip configuration
noUiSlider.create(element, {
    start: [20, 50, 80],
    range: { min: 0, max: 100 },
    tooltips: [
        true,                    // Default tooltip for first handle
        false,                   // No tooltip for second handle
        { to: value => value.toFixed(1) + '%' }  // Custom for third handle
    ]
});

// Access and modify tooltips
const tooltips = slider.noUiSlider.getTooltips();
tooltips[0].style.backgroundColor = 'red';

// Remove tooltips
slider.noUiSlider.removeTooltips();

Handle Management

Access to slider handle elements for advanced customization.

getOrigins()

Returns array of handle origin elements.

/**
 * Get array of handle origin DOM elements
 * @returns Array of handle origin elements
 */
slider.noUiSlider.getOrigins(): HTMLElement[];

Usage Examples:

// Get handle elements
const handles = slider.noUiSlider.getOrigins();

// Customize handle appearance
handles[0].style.backgroundColor = 'red';
handles[1].style.backgroundColor = 'blue';

// Add custom event listeners to handles
handles.forEach((handle, index) => {
    handle.addEventListener('mouseenter', function() {
        console.log('Hovering over handle', index);
    });
});

// Add custom content to handles
handles[0].innerHTML = '<span class="handle-label">Min</span>';
handles[1].innerHTML = '<span class="handle-label">Max</span>';

Advanced Styling

CSS Classes

Access to default CSS classes for styling customization.

noUiSlider.cssClasses: CSSClasses;

Usage Examples:

// Access default CSS classes
console.log(noUiSlider.cssClasses.handle);  // "handle"
console.log(noUiSlider.cssClasses.connect); // "connect"

// Use for custom styling
const handleClass = 'noUi-' + noUiSlider.cssClasses.handle;
document.querySelectorAll('.' + handleClass).forEach(handle => {
    handle.style.borderRadius = '50%';
});

Custom CSS Classes

Override default CSS classes during creation.

noUiSlider.create(element, {
    start: [20, 80],
    range: { min: 0, max: 100 },
    cssPrefix: 'custom-',
    cssClasses: {
        target: 'slider',
        base: 'slider-base',
        handle: 'slider-handle',
        connect: 'slider-connect'
    }
});

Target Element Access

target Property

Reference to the original DOM element.

/**
 * Reference to the slider's target DOM element
 */
slider.noUiSlider.target: HTMLElement;

Usage Examples:

// Access target element
const targetElement = slider.noUiSlider.target;

// Add custom classes
targetElement.classList.add('premium-slider');

// Custom styling
targetElement.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)';

// Data attributes
targetElement.setAttribute('data-slider-id', 'price-range');

Practical UI Enhancement Patterns

Custom Pip Labels

// Currency pips with custom labels
slider.noUiSlider.pips({
    mode: 'values',
    values: [0, 1000, 2500, 5000, 10000],
    format: {
        to: function(value) {
            if (value >= 1000) {
                return '$' + (value / 1000) + 'k';
            }
            return '$' + value;
        }
    },
    filter: function(value, type) {
        // Large pips for major values
        if ([0, 5000, 10000].includes(value)) {
            return 1;  // Large pip
        }
        return 0;  // Normal pip
    }
});

Interactive Tooltips

// Create slider with tooltips
noUiSlider.create(element, {
    start: [20, 80],
    range: { min: 0, max: 100 },
    tooltips: true,
    connect: true
});

// Enhance tooltips with click handlers
const tooltips = slider.noUiSlider.getTooltips();
tooltips.forEach((tooltip, index) => {
    if (tooltip) {
        tooltip.addEventListener('click', function() {
            const newValue = prompt('Enter new value:');
            if (newValue !== null) {
                slider.noUiSlider.setHandle(index, parseFloat(newValue));
            }
        });
        
        tooltip.style.cursor = 'pointer';
        tooltip.title = 'Click to edit value';
    }
});

Dynamic Pip Updates

// Update pips based on zoom level
function updatePipsForZoom(zoomLevel) {
    const pipConfigs = {
        1: { mode: 'count', values: 5, density: 2 },
        2: { mode: 'count', values: 10, density: 3 },
        3: { mode: 'range', density: 1 }
    };
    
    slider.noUiSlider.removePips();
    slider.noUiSlider.pips(pipConfigs[zoomLevel]);
}

// Zoom controls
document.getElementById('zoom-in').addEventListener('click', () => {
    currentZoom = Math.min(3, currentZoom + 1);
    updatePipsForZoom(currentZoom);
});

Handle Labeling

// Add persistent labels to handles
const handles = slider.noUiSlider.getOrigins();
const labels = ['Minimum', 'Maximum'];

handles.forEach((handle, index) => {
    const label = document.createElement('div');
    label.className = 'handle-label';
    label.textContent = labels[index];
    label.style.position = 'absolute';
    label.style.top = '-30px';
    label.style.left = '50%';
    label.style.transform = 'translateX(-50%)';
    label.style.fontSize = '12px';
    label.style.fontWeight = 'bold';
    
    handle.appendChild(label);
});

Progress Indication

// Visual progress indicator
function updateProgress() {
    const values = slider.noUiSlider.get();
    const progress = parseFloat(values[0]); // Use first handle
    const maxValue = slider.noUiSlider.options.range.max;
    const percentage = (progress / maxValue) * 100;
    
    // Update progress bar
    document.getElementById('progress-bar').style.width = percentage + '%';
    
    // Update progress text
    document.getElementById('progress-text').textContent = 
        Math.round(percentage) + '% Complete';
}

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

Conditional Styling

// Style based on values
function updateSliderStyling(values) {
    const range = parseFloat(values[1]) - parseFloat(values[0]);
    const target = slider.noUiSlider.target;
    
    // Remove previous state classes
    target.classList.remove('narrow-range', 'wide-range', 'full-range');
    
    // Add appropriate class based on range
    if (range < 20) {
        target.classList.add('narrow-range');
    } else if (range > 80) {
        target.classList.add('wide-range');
    } else if (range === 100) {
        target.classList.add('full-range');
    }
}

slider.noUiSlider.on('update', function(values) {
    updateSliderStyling(values);
});

Accessibility Enhancements

// Enhance accessibility
const handles = slider.noUiSlider.getOrigins();
handles.forEach((handle, index) => {
    const handleElement = handle.querySelector('.noUi-handle');
    
    // Custom aria labels
    handleElement.setAttribute('aria-label', `Handle ${index + 1}`);
    
    // Descriptive titles
    handleElement.setAttribute('title', `Drag to adjust value ${index + 1}`);
});

// Update aria-valuetext with formatted values
slider.noUiSlider.on('update', function(values, handle) {
    const handleElement = handles[handle].querySelector('.noUi-handle');
    handleElement.setAttribute('aria-valuetext', 
        `Current value: ${values[handle]}`);
});

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