- Color Picker
- Using dates
- 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
- Working with huge numbers
- Create a toggle
- Block the edges of a slider
- Connect to the center of a slider
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.
var resultElement = document.getElementById('result');
var sliders = document.getElementsByClassName('sliders');
var colors = [0, 0, 0];
[].slice.call(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;
});
});
#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;
}
Working with dates
This example shows how to convert dates to numerical ranges, and then use the update
event to display them in a pretty format.
We'll be creating timestamps from strings. In order to do this easily, we'll define a new helper function. This function accepts a string, creates a new Date
and then returns it as a timestamp.
// Create a new date from a string, return as a timestamp.
function timestamp(str) {
return new Date(str).getTime();
}
var dateSlider = document.getElementById('slider-date');
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
})
});
var dateValues = [
document.getElementById('event-start'),
document.getElementById('event-end')
];
dateSlider.noUiSlider.on('update', function (values, handle) {
dateValues[handle].innerHTML = formatDate(new Date(+values[handle]));
});
The nth
function was borrowed from this StackOverflow question.
// Create a list of day and month names.
var weekdays = [
"Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday", "Friday",
"Saturday"
];
var months = [
"January", "February", "March",
"April", "May", "June", "July",
"August", "September", "October",
"November", "December"
];
// Append a suffix to dates.
// Example: 23 => 23rd, 1 => 1st.
function nth(d) {
if (d > 3 && d < 21) return 'th';
switch (d % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
// Create a string representation of the date.
function formatDate(date) {
return weekdays[date.getDay()] + ", " +
date.getDate() + nth(date.getDate()) + " " +
months[date.getMonth()] + " " +
date.getFullYear();
}
Using HTML5 input elements
noUiSlider's 'update'
method is useful for synchronizing with other elements, such as <input>
(type="number"
) and <select>
.
<option>
elementsvar 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);
}
var html5Slider = document.getElementById('html5');
noUiSlider.create(html5Slider, {
start: [10, 30],
connect: true,
range: {
'min': -20,
'max': 40
}
});
<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]);
});
#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.
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]
}
});
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.
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';
});
Crossupdate
functionfunction 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);
}
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];
});
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. noUiSlider 11 adds a data-value
attribute to all .noUi-value
elements that makes this easy.
var pipsSlider = document.getElementById('slider-pips');
noUiSlider.create(pipsSlider, {
range: {
min: 0,
max: 100
},
start: [50],
pips: {mode: 'count', values: 5}
});
var pips = pipsSlider.querySelectorAll('.noUi-value');
function clickOnPip() {
var value = Number(this.getAttribute('data-value'));
pipsSlider.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.
.noUi-tooltip {
display: none;
}
.noUi-active .noUi-tooltip {
display: block;
}
Colored Connect Elements
noUiSlider's connect elements can be individually colored or otherwise styled.
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.
We'll listen to keydown on the input element, and pass the event to a function so we can read the code that identifies the key.
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];
});
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.
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]
});
var skipValues = [
document.getElementById('skip-value-lower'),
document.getElementById('skip-value-upper')
];
skipSlider.noUiSlider.on('update', function (values, handle) {
skipValues[handle].innerHTML = values[handle];
});
Using the slider with huge numbers
If you are working with arbitrarily large numbers, you should not use these directly in noUiSlider as you'll run into some JavaScript limitations. Instead, you should map your values to an array of string values.
Numbers is JavaScript are "double precision floats", which can store numbers up to 2^53 (9007199254740992) precisely.
For reference, see this StackOverflow question, or issue #427 filed on GitHub.
As an example, see the 'range'
option for a RAM selector offering 14 steps from 512MB to 8GB. The 'step'
are omitted for clarity. The values are provided as bytes.
A better solution would be to abstract the byte values away from the slider, looking up the byte values in an array. This keeps the slider configuration simple and prevents issues with floating point precision.
(These values fit within the limit just fine, but demonstrate the point really well!)
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
}
var bigValueSlider = document.getElementById('slider-huge');
var bigValueSpan = document.getElementById('huge-value');
noUiSlider.create(bigValueSlider, {
start: 0,
step: 1,
format: wNumb({
decimals: 0
}),
range: {
min: 0,
max: 13
}
});
// Note how these are 'string' values, not numbers.
var range = [
'0', '2097152', '4194304',
'8388608', '16777216', '33554432',
'67108864', '134217728', '268435456',
'536870912', '1073741824',
'2147483648', '4294967296',
'8589934592'
];
bigValueSlider.noUiSlider.on('update', function (values, handle) {
bigValueSpan.innerHTML = range[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
.
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');
}
});
#slider-toggle {
height: 50px;
}
#slider-toggle.off .noUi-handle {
border-color: red;
}
Soft limits
If you want 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.
var softSlider = document.getElementById('soft');
noUiSlider.create(softSlider, {
start: 50,
range: {
min: 0,
max: 100
},
pips: {
mode: 'values',
values: [20, 80],
density: 4
}
});
change
eventsoftSlider.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.
An example of how to implement this is available as a JSFiddle.