noUiSlider Range and Handles Reading & Setting Values Formatting Options Tapping, Dragging & Fixed Ranges Examples Events Scale/Pips Updating, Disabling & Styling Download

- Using an array of values
- Number formatting
- Color picker
- Styling
- Interacting with Pips
- Using dates
- Merging overlapping tooltips
- Working with HTML5 input types
- Using non linear ranges
- Locking two sliders together
- Moving the slider by clicking pips
- Only showing tooltips when sliding handles
- Colored connect elements
- Changing the slider value by keypress
- Skipping values on a slider
- Create a toggle
- Block the edges of a slider
- Connect to the center of a slider

Can't find something? See the full options reference.
§

# Using an array of values

noUiSlider allows configuration of complex non-linear ranges with various constraints on positioning and stepping. This setup might be overly complicated to display a slider with a pre-determined available selection of values.

For such a scenario, the `format` option can be used. This option provides a wrapper around the `get` and `set` methods, allowing for a simpler interface.

    var valuesSlider = document.getElementById('values-slider');
    var valuesForSlider = [1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32]; // 16 values

    var format = {
        to: function(value) {
            return valuesForSlider[Math.round(value)];
        },
        from: function (value) {
            return valuesForSlider.indexOf(Number(value));
        }
    };

    noUiSlider.create(valuesSlider, {
        start: [8, 24],
        // A linear range from 0 to 15 (16 values)
        range: { min: 0, max: valuesForSlider.length - 1 },
        // steps of 1
        step: 1,
        tooltips: true,
        format: format,
        pips: { mode: 'steps', format: format },
    });

    // The display values can be used to control the slider
    valuesSlider.noUiSlider.set(['7', '28']);

What not to do

    // This approach is not recommended
    range: {
    	  'min': 0,
    	 '7.6%': 2097152,
    	'15.3%': 4194304,
    	'23.0%': 8388608,
    	'30.7%': 16777216,
    	'38.4%': 33554432,
    	'46.1%': 67108864,
    	'53.8%': 134217728,
    	'61.5%': 268435456,
    	'69.2%': 536870912,
    	'76.9%': 1073741824,
    	'84.6%': 2147483648,
    	'92.3%': 4294967296,
    	  'max': 8589934592
    }

Using arbitrary (string) values

    var arbitraryValuesSlider = document.getElementById('arbitrary-values-slider');
    var arbitraryValuesForSlider = ['128MB', '256MB', '1GB', '8GB', '16GB', '32GB'];

    var format = {
        to: function(value) {
            return arbitraryValuesForSlider[Math.round(value)];
        },
        from: function (value) {
            return arbitraryValuesForSlider.indexOf(value);
        }
    };

    noUiSlider.create(arbitraryValuesSlider, {
        // start values are parsed by 'format'
        start: ['1GB', '16GB'],
        range: { min: 0, max: arbitraryValuesForSlider.length - 1 },
        step: 1,
        tooltips: true,
        format: format,
        pips: { mode: 'steps', format: format, density: 50 },
    });

§

# Number formatting

noUiSlider has a `format` option to configure both incoming and outgoing formatting.

In this example, the slider format uses no decimals. The `tooltips` use one decimal.

The `from` function takes a formatted value to parse. It gets a string and should return a number.

The `to` function takes a number to format. It gets a number and should return a string.

noUiSlider can optionally be used with the `wNumb` library for formatting. See the documentation on number formatting for more details.

    var formatForSlider = {
        from: function (formattedValue) {
            return Number(formattedValue);
        },
        to: function(numericValue) {
            return Math.round(numericValue);
        }
    };

Slider with formatting

    var formatSlider = document.getElementById('formatting-slider');

    noUiSlider.create(formatSlider, {
        // Values are parsed as numbers using the "from" function in "format"
        start: ['20.0', '80.0'],
        range: {
            'min': 0,
            'max': 100
        },
        format: formatForSlider,
        tooltips: {
            // tooltips are output only, so only a "to" is needed
            to: function(numericValue) {
                return numericValue.toFixed(1);
            }
        }
    });

    // Values are parsed as numbers using the "from" function in "format"
    formatSlider.noUiSlider.set(['25.666', '57.66']);

    var formatValues = [
        document.getElementById('formatting-start'),
        document.getElementById('formatting-end')
    ];

    formatSlider.noUiSlider.on('update', function (values, handle, unencoded) {
        // "values" has the "to" function from "format" applied
        // "unencoded" contains the raw numerical slider values
        formatValues[handle].innerHTML = values[handle] + '
    No format: ' + unencoded[handle];
    });

§

# Colorpicker

We'll initialize all sliders with the same options, and use the `update` callback to keep to color in sync with the slider values.

This callback fires any time a slider value updates.

Colorpicker

    var resultElement = document.getElementById('result');
    var sliders = document.querySelectorAll('.sliders');
    var colors = [0, 0, 0];

    sliders.forEach(function (slider, index) {

        noUiSlider.create(slider, {
            start: 127,
            connect: [true, false],
            orientation: "vertical",
            range: {
                'min': 0,
                'max': 255
            },
            format: wNumb({
                decimals: 0
            })
        });

        // Bind the color changing function to the update event.
        slider.noUiSlider.on('update', function () {

            colors[index] = slider.noUiSlider.get();

            var color = 'rgb(' + colors.join(',') + ')';

            resultElement.style.background = color;
            resultElement.style.color = color;
        });
    });

CSS

    #red, #green, #blue {
    	margin: 10px;
    	display: inline-block;
    	height: 200px;
    }

    #colorpicker {
    	height: 240px;
    	width: 310px;
    	margin: 0 auto;
    	padding: 10px;
    	border: 1px solid #BFBFBF;
    }

    #result {
    	margin: 60px 26px;
    	height: 100px;
    	width: 100px;
    	display: inline-block;
    	vertical-align: top;
    	color: rgb(127, 127, 127);
    	background: rgb(127, 127, 127);
    	border: 1px solid #fff;
    	box-shadow: 0 0 10px;
    }

    #red .noUi-connect {
    	background: #c0392b;
    }

    #green .noUi-connect {
    	background: #27ae60;
    }

    #blue .noUi-connect {
    	background: #2980b9;
    }

§

# Styling

noUiSlider, by default, comes with a design with large handles and box shadows. In general, larger handles make range sliders easier to use on mobile devices and touch screens.

It is recommended to use the default stylesheet, overriding where necessary, as a starting point when re-styling noUiSlider.

When the slider handles are restyled to be smaller, `.noUi-touch-area` should be styled to be larger, to be easier to click or touch. The sliders in this example show the larger touch area when hovered.

`padding` can be added to `.noUi-target` to contain handles within the slider's width.

See the documentation on styling for further tips and an overview of classes.

Default styling
Fit handles within slider
Don't overlap handles
Rounded styling
Square styling

    <div id="slider"></div>
    <div id="slider-fit"></div>
    <div id="slider-no-overlap"></div>
    <div class="slider-styled" id="slider-round"></div>
    <div class="slider-styled" id="slider-square"></div>

Don't overlap handles

    #slider-no-overlap .noUi-handle-lower {
        right: 0;
    }
    #slider-no-overlap .noUi-handle-upper {
        right: -34px;
    }

Fit handles withing the slider

    #slider-fit {
        padding: 0 16px;
    }

Style resets

    .slider-styled,
    .slider-styled .noUi-handle {
        box-shadow: none;
    }

    /* Hide markers on slider handles */
    .slider-styled .noUi-handle::before,
    .slider-styled .noUi-handle::after {
        display: none;
    }

Styling the touch area

    .slider-styled .noUi-handle .noUi-touch-area {
        border: 1px solid transparent;
        position: absolute;
        top: -10px;
        left: -10px;
        right: -10px;
        bottom: -10px;
        width: auto;
        height: auto;
    }

    /* Show a border when hovering the area the handle responds to */
    .slider-styled .noUi-handle:hover .noUi-touch-area {
        border: 1px dashed #7f7f7f;
    }

Styling round slider

    #slider-round {
        height: 10px;
    }

    #slider-round .noUi-connect {
        background: #c0392b;
    }

    #slider-round .noUi-handle {
        height: 18px;
        width: 18px;
        top: -5px;
        right: -9px; /* half the width */
        border-radius: 9px;
    }

Styling square slider

    #slider-square {
        border-radius: 0;
    }

    #slider-square .noUi-handle {
        border-radius: 0;
        background: #2980b9;
        height: 18px;
        width: 18px;
        top: -1px;
        right: -9px;
    }

§

# Interacting with pips

When using the pips feature, several interactions can be added. Pips can also be styled.

This example shows pips being displayed vertically, and highlighted when the slider handle is at that value.

Have a look at the example on moving the slider by clicking pips.

Slider with pips

    var pipsSlider = document.getElementById('slider-pips');

    noUiSlider.create(pipsSlider, {
        start: [20, 80],
        margin: 20,
        connect: true,
        range: {
            'min': 0,
            'max': 100
        },
        step: 20,
        pips: {
            mode: 'steps',
            density: 3
        }
    });

    var activePips = [null, null];

    pipsSlider.noUiSlider.on('update', function (values, handle) {
        // Remove the active class from the current pip
        if (activePips[handle]) {
            activePips[handle].classList.remove('active-pip');
        }

        // Match the formatting for the pip
        var dataValue = Math.round(values[handle]);

        // Find the pip matching the value
        activePips[handle] = pipsSlider.querySelector('.noUi-value[data-value="' + dataValue + '"]');

        // Add the active class
        if (activePips[handle]) {
            activePips[handle].classList.add('active-pip');
        }
    });

Vertical Pips

    #slider-pips .noUi-value-horizontal {
        -webkit-transform: translate(-50%, 25px);
        transform: translate(-50%, 25px);
        writing-mode: vertical-lr;
    }

    .active-pip {
        font-weight: bold;
        color: red;
    }

§

# Working with dates

This example shows how to convert dates to numerical ranges, and then use the `update` event to display them.

Setup

    var dateSlider = document.getElementById('slider-date');

    function timestamp(str) {
        return new Date(str).getTime();
    }

    noUiSlider.create(dateSlider, {
    // Create two timestamps to define a range.
        range: {
            min: timestamp('2010'),
            max: timestamp('2016')
        },

    // Steps of one week
        step: 7 * 24 * 60 * 60 * 1000,

    // Two more timestamps indicate the handle starting positions.
        start: [timestamp('2011'), timestamp('2015')],

    // No decimals
        format: wNumb({
            decimals: 0
        })
    });

Update event

    var dateValues = [
        document.getElementById('event-start'),
        document.getElementById('event-end')
    ];

    var formatter = new Intl.DateTimeFormat('en-GB', {
        dateStyle: 'full'
    });

    dateSlider.noUiSlider.on('update', function (values, handle) {
        dateValues[handle].innerHTML = formatter.format(new Date(+values[handle]));
    });

§

# Merging overlapping tooltips

Issue #1032 asks to merge overlapping tooltips. As this feature is outside the scope of the tooltips-feature in noUiSlider, this example can be used to implement this feature using the event system.

Initializing the slider

    var mergingTooltipSlider = document.getElementById('merging-tooltips');

    noUiSlider.create(mergingTooltipSlider, {
        start: [20, 32, 50, 70, 80, 90],
        connect: true,
        tooltips: [false, true, true, true, true, true],
        range: {
            'min': 0,
            'max': 100
        }
    });

    mergeTooltips(mergingTooltipSlider, 15, ' - ');

Merging overlapping tooltips

    /**
     * @param slider HtmlElement with an initialized slider
     * @param threshold Minimum proximity (in percentages) to merge tooltips
     * @param separator String joining tooltips
     */
    function mergeTooltips(slider, threshold, separator) {

        var textIsRtl = getComputedStyle(slider).direction === 'rtl';
        var isRtl = slider.noUiSlider.options.direction === 'rtl';
        var isVertical = slider.noUiSlider.options.orientation === 'vertical';
        var tooltips = slider.noUiSlider.getTooltips();
        var origins = slider.noUiSlider.getOrigins();

        // Move tooltips into the origin element. The default stylesheet handles this.
        tooltips.forEach(function (tooltip, index) {
            if (tooltip) {
                origins[index].appendChild(tooltip);
            }
        });

        slider.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) {

            var pools = [[]];
            var poolPositions = [[]];
            var poolValues = [[]];
            var atPool = 0;

            // Assign the first tooltip to the first pool, if the tooltip is configured
            if (tooltips[0]) {
                pools[0][0] = 0;
                poolPositions[0][0] = positions[0];
                poolValues[0][0] = values[0];
            }

            for (var i = 1; i < positions.length; i++) {
                if (!tooltips[i] || (positions[i] - positions[i - 1]) > threshold) {
                    atPool++;
                    pools[atPool] = [];
                    poolValues[atPool] = [];
                    poolPositions[atPool] = [];
                }

                if (tooltips[i]) {
                    pools[atPool].push(i);
                    poolValues[atPool].push(values[i]);
                    poolPositions[atPool].push(positions[i]);
                }
            }

            pools.forEach(function (pool, poolIndex) {
                var handlesInPool = pool.length;

                for (var j = 0; j < handlesInPool; j++) {
                    var handleNumber = pool[j];

                    if (j === handlesInPool - 1) {
                        var offset = 0;

                        poolPositions[poolIndex].forEach(function (value) {
                            offset += 1000 - value;
                        });

                        var direction = isVertical ? 'bottom' : 'right';
                        var last = isRtl ? 0 : handlesInPool - 1;
                        var lastOffset = 1000 - poolPositions[poolIndex][last];
                        offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset;

                        // Center this tooltip over the affected handles
                        tooltips[handleNumber].innerHTML = poolValues[poolIndex].join(separator);
                        tooltips[handleNumber].style.display = 'block';
                        tooltips[handleNumber].style[direction] = offset + '%';
                    } else {
                        // Hide this tooltip
                        tooltips[handleNumber].style.display = 'none';
                    }
                }
            });
        });
    }

§

# Using HTML5 input elements

noUiSlider's `'update'` method is useful for synchronizing with other elements, such as `<input>` (`type="number"`) and `<select>`.

Appending `<option>` elements

    var select = document.getElementById('input-select');

    // Append the option elements
    for (var i = -20; i <= 40; i++) {

        var option = document.createElement("option");
        option.text = i;
        option.value = i;

        select.appendChild(option);
    }

Initializing the slider

    var html5Slider = document.getElementById('html5');

    noUiSlider.create(html5Slider, {
        start: [10, 30],
        connect: true,
        range: {
            'min': -20,
            'max': 40
        }
    });

Updating the `<select>` and `<input>`

    var inputNumber = document.getElementById('input-number');

    html5Slider.noUiSlider.on('update', function (values, handle) {

        var value = values[handle];

        if (handle) {
            inputNumber.value = value;
        } else {
            select.value = Math.round(value);
        }
    });

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

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

Example CSS

    #input-select,
    #input-number {
    	padding: 7px;
    	margin: 15px 5px 5px;
    	width: 70px;
    }

§

# Non linear slider

One of noUiSlider's core features is the ability to divide the range in a non-linear fashion. Stepping can be applied. This example shows where the handles are on the slider range in values and percentages.

Setting up the slider

    var nonLinearSlider = document.getElementById('nonlinear');

    noUiSlider.create(nonLinearSlider, {
        connect: true,
        behaviour: 'tap',
        start: [500, 4000],
        range: {
            // Starting at 500, step the value by 500,
            // until 4000 is reached. From there, step by 1000.
            'min': [0],
            '10%': [500, 500],
            '50%': [4000, 1000],
            'max': [10000]
        }
    });

Read the slider value and the left offset

    var nodes = [
        document.getElementById('lower-value'), // 0
        document.getElementById('upper-value')  // 1
    ];

    // Display the slider value and how far the handle moved
    // from the left edge of the slider.
    nonLinearSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
        nodes[handle].innerHTML = values[handle] + ', ' + positions[handle].toFixed(2) + '%';
    });

§

# Locking sliders together

Two cross-updating sliders can be created using a combination of the `change` and `slide` events.

Setup and button clicks

    var lockedState = false;
    var lockedSlider = false;
    var lockedValues = [60, 80];

    var slider1 = document.getElementById('slider1');
    var slider2 = document.getElementById('slider2');

    var lockButton = document.getElementById('lockbutton');
    var slider1Value = document.getElementById('slider1-span');
    var slider2Value = document.getElementById('slider2-span');

    // When the button is clicked, the locked state is inverted.
    lockButton.addEventListener('click', function () {
        lockedState = !lockedState;
        this.textContent = lockedState ? 'unlock' : 'lock';
    });

The `Crossupdate` function

    function crossUpdate(value, slider) {

        // If the sliders aren't interlocked, don't
        // cross-update.
        if (!lockedState) return;

        // Select whether to increase or decrease
        // the other slider value.
        var a = slider1 === slider ? 0 : 1;

        // Invert a
        var b = a ? 0 : 1;

        // Offset the slider value.
        value -= lockedValues[b] - lockedValues[a];

        // Set the value
        slider.noUiSlider.set(value);
    }

Initializing the sliders

    noUiSlider.create(slider1, {
        start: 60,

        // Disable animation on value-setting,
        // so the sliders respond immediately.
        animate: false,
        range: {
            min: 50,
            max: 100
        }
    });

    noUiSlider.create(slider2, {
        start: 80,
        animate: false,
        range: {
            min: 50,
            max: 100
        }
    });

    slider1.noUiSlider.on('update', function (values, handle) {
        slider1Value.innerHTML = values[handle];
    });

    slider2.noUiSlider.on('update', function (values, handle) {
        slider2Value.innerHTML = values[handle];
    });

Linking the sliders together

    function setLockedValues() {
        lockedValues = [
            Number(slider1.noUiSlider.get()),
            Number(slider2.noUiSlider.get())
        ];
    }

    slider1.noUiSlider.on('change', setLockedValues);
    slider2.noUiSlider.on('change', setLockedValues);

    slider1.noUiSlider.on('slide', function (values, handle) {
        crossUpdate(values[handle], slider2);
    });

    slider2.noUiSlider.on('slide', function (values, handle) {
        crossUpdate(values[handle], slider1);
    });

§

# Moving the slider by clicking pips

Issue #733 asks about clicking pips to move the slider to their value. All `.noUi-value` elements have a `data-value` attribute to facilitate this.

Setup

    var clickPipsSlider = document.getElementById('slider-click-pips');

    noUiSlider.create(clickPipsSlider, {
        range: {
            min: 0,
            max: 100
        },
        start: [50],
        pips: {mode: 'count', values: 5}
    });

    var pips = clickPipsSlider.querySelectorAll('.noUi-value');

    function clickOnPip() {
        var value = Number(this.getAttribute('data-value'));
        clickPipsSlider.noUiSlider.set(value);
    }

    for (var i = 0; i < pips.length; i++) {

        // For this example. Do this in CSS!
        pips[i].style.cursor = 'pointer';
        pips[i].addEventListener('click', clickOnPip);
    }

§

# Only showing tooltips when sliding handles

Issue #836 requested a way to toggle tooltips after slider creation. This effect can be achieved by using the `.noUi-active` class to show and hide the tooltips. No additional JavaScript is involved.

    #slider-hide .noUi-tooltip {
        display: none;
    }
    #slider-hide .noUi-active .noUi-tooltip {
        display: block;
    }

§

# Colored Connect Elements

noUiSlider's connect elements can be individually colored or otherwise styled.

Slider setup

    var slider = document.getElementById('slider-color');

    noUiSlider.create(slider, {
        start: [4000, 8000, 12000, 16000],
        connect: [false, true, true, true, true],
        range: {
            'min': [2000],
            'max': [20000]
        }
    });

    var connect = slider.querySelectorAll('.noUi-connect');
    var classes = ['c-1-color', 'c-2-color', 'c-3-color', 'c-4-color', 'c-5-color'];

    for (var i = 0; i < connect.length; i++) {
        connect[i].classList.add(classes[i]);
    }

    .c-1-color { background: red; }
    .c-2-color { background: yellow; }
    .c-3-color { background: green; }
    .c-4-color { background: blue; }
    .c-5-color { background: purple; }

§

# Using the steps API

noUiSlider provides a `steps` API to determine the previous and next steps for a handle. In this example, pressing the keyboard arrow keys will increase/decrease the slider by one step.

Use of this API is not necessary for linear sliders, as the step is constant in that case.

A `keydown` listener is added to the input element, passing the event to a function to read the `code` that identifies the key.

Initializing the slider and linking the input

    var stepsSlider = document.getElementById('steps-slider');
    var input0 = document.getElementById('input-with-keypress-0');
    var input1 = document.getElementById('input-with-keypress-1');
    var inputs = [input0, input1];

    noUiSlider.create(stepsSlider, {
        start: [20, 80],
        connect: true,
        tooltips: [true, wNumb({decimals: 1})],
        range: {
            'min': [0],
            '10%': [10, 10],
            '50%': [80, 50],
            '80%': 150,
            'max': 200
        }
    });

    stepsSlider.noUiSlider.on('update', function (values, handle) {
        inputs[handle].value = values[handle];
    });

Listen to `keypress` on the input

    // Listen to keydown events on the input field.
    inputs.forEach(function (input, handle) {

        input.addEventListener('change', function () {
            stepsSlider.noUiSlider.setHandle(handle, this.value);
        });

        input.addEventListener('keydown', function (e) {

            var values = stepsSlider.noUiSlider.get();
            var value = Number(values[handle]);

            // [[handle0_down, handle0_up], [handle1_down, handle1_up]]
            var steps = stepsSlider.noUiSlider.steps();

            // [down, up]
            var step = steps[handle];

            var position;

            // 13 is enter,
            // 38 is key up,
            // 40 is key down.
            switch (e.which) {

                case 13:
                    stepsSlider.noUiSlider.setHandle(handle, this.value);
                    break;

                case 38:

                    // Get step to go increase slider value (up)
                    position = step[1];

                    // false = no step is set
                    if (position === false) {
                        position = 1;
                    }

                    // null = edge of slider
                    if (position !== null) {
                        stepsSlider.noUiSlider.setHandle(handle, value + position);
                    }

                    break;

                case 40:

                    position = step[0];

                    if (position === false) {
                        position = 1;
                    }

                    if (position !== null) {
                        stepsSlider.noUiSlider.setHandle(handle, value - position);
                    }

                    break;
            }
        });
    });

§

# Skipping steps

When using a stepped slider, your configuration may require that certain steps aren't available. Using the `snap` feature, this effect can be created.

Notice how `40` and `80` can't be selected on the slider.

Initialize a snapping slider

    var skipSlider = document.getElementById('skipstep');

    noUiSlider.create(skipSlider, {
        range: {
            'min': 0,
            '10%': 10,
            '20%': 20,
            '30%': 30,
            // Nope, 40 is no fun.
            '50%': 50,
            '60%': 60,
            '70%': 70,
            // I never liked 80.
            '90%': 90,
            'max': 100
        },
        snap: true,
        start: [20, 90]
    });

Read the slider values

    var skipValues = [
        document.getElementById('skip-value-lower'),
        document.getElementById('skip-value-upper')
    ];

    skipSlider.noUiSlider.on('update', function (values, handle) {
        skipValues[handle].innerHTML = values[handle];
    });

§

# Creating a toggle

Many application interfaces have options that can be turned on or off using switches. noUiSlider is well suited for this, especially because of the wide touch support.

The `update` event can be used to keep track of changes to the handle. We'll set the range to `[0, 1]`, which leaves one step of `1`.

Toggle

    var toggleSlider = document.getElementById('slider-toggle');

    noUiSlider.create(toggleSlider, {
        orientation: "vertical",
        start: 0,
        range: {
            'min': [0, 1],
            'max': 1
        },
        format: wNumb({
            decimals: 0
        })
    });

    toggleSlider.noUiSlider.on('update', function (values, handle) {
        if (values[handle] === '1') {
            toggleSlider.classList.add('off');
        } else {
            toggleSlider.classList.remove('off');
        }
    });

CSS

    #slider-toggle {
    	height: 50px;
    }
    #slider-toggle.off .noUi-handle {
    	border-color: red;
    }

§

# Soft limits

To disable the edges of a slider, the set event can be used to reset the value if a limit is passed. Note how the handle 'bounces back' when it is released below `20` or above `80`. noUiSlider also supports disabling edges altogether, which can be done using the padding option.

Setting up the slider

    var softSlider = document.getElementById('soft');

    noUiSlider.create(softSlider, {
        start: 50,
        range: {
            min: 0,
            max: 100
        },
        pips: {
            mode: 'values',
            values: [20, 80],
            density: 4
        }
    });

Resetting using the `change` event

    softSlider.noUiSlider.on('change', function (values, handle) {
        if (values[handle] < 20) {
            softSlider.noUiSlider.set(20);
        } else if (values[handle] > 80) {
            softSlider.noUiSlider.set(80);
        }
    });

§

# Slider with connect from the center

Issue #371 requested a slider with the connect option originating from the slider center. This can be implemented by hiding one of the handles and using the `"unconstrained"` behaviour.

    #from-center .noUi-handle[data-handle="0"] {
        display: none;
    }

    var fromCenterSlider = document.getElementById('from-center');

    noUiSlider.create(fromCenterSlider, {
        start: [50, 80],
        behaviour: 'unconstrained',
        connect: true,
        range: {
            'min': 0,
            'max': 100
        }
    });