Among the many new form elements that HTML5 has introduced, range
is perhaps the oddest but also the most generally useful. Previously requiring a great deal of JavaScript coding or a framework to generate, you can now create a slide control natively in all modern browsers with a single element.
range
is particularly useful for forms in which three conditions exist:
- A wide range of numbers is equally available to the user.
- There is a known upper and lower limit to the range.
- The user is expected to “play” with their input, adjusting it frequently.
Think of a shipping form that judges the price of posting a package based on weight. The lowest weight might be an envelope with a sheet of paper (about 10 grams), with an upper limit of 2 kilos. Let’s say the price of shipping changes with every 100 grams added or removed. It’s very likely that the user will want to find the most economic deal, and could be expected to play within the weight limits for their package. In that case, appropriate markup could be:
<label for="weight">Weight</label>
<input type="range" id="weight" min="10" value="10" max="2000" step="100">
The alternatives – a drop-down select option or a number
input - would be irritating to use, but a range
slider is perfect.
In contrast, take a restaurant booking form, where the number of guests has an obvious upper and lower limit, but a user would be expected to approach the form with a set number already in mind. In that case, “playing” with a slider to find the right number of seats to book for the evening would be frustrating, making a number
input would be far more appropriate.
Setup, Accessibility And Attributes
At the basic level, a range
input should have at least three attributes: min
(the lowest accepted value), max
(the highest) and value
(the default, or starting value). Form inputs should have associated label
elements, meaning that the range should also have an id
attribute in most cases. Let’s use the example of a volume control:
<label for="fader">Volume</label>
<input type="range" min="0" max="5" value="3" id="fader">
Which produces:
Note that the slider UI has a few unique features:
- The slider button “snaps” as it is moved. By default, these snapping points correspond to the location of whole integers in the range.
- When focused, the button can be moved with up/down cursor keys, just like the
number
input. (Not all browsers support this behavior yet). - The extent of the slider does not change according to the value of
max
; instead, it is adjusted by changing thewidth
of the element in CSS. The relative position of the snap points will be adjusted in response to this change.
The “snapping” behavior of the range button can be particularly confusing when min
and max
are relatively close. As an example, let’s set of the range of the volume fader between 0 and 1:
<input type="range" min="0" max="1" value="1" id="fader">
The button now snaps to the terminus of the range, with no intermediate positions. The solution is to introduce more granularity by using a step
attribute value:
<input type="range" min="0" max="1" value="1" id="fader" step=".05">
Note that just like the number
input, step
can be used to allow only certain integers in a range: you can accept only even numbers between min
and max
by setting step=2
, for example. Also note that the use of HTML5 shortcuts is limited in this case: you must use quotes around values like 0.5 in order for the code to be valid. A value of all
for step
makes the slider register floating-point values, and is the finest level of control.
Show and Tell
The range
input doesn’t present a numerical readout of its value by default. To remedy that, we use the <output>
element:
<label for="fader">Volume</label>
<input type="range" min="0" max="100" value="50" id="fader"
step="1" oninput="outputUpdate(value)">
<output for="fader" id="volume">50</output>
Together with a little JavaScript:
function outputUpdate(vol) {
document.querySelector('#volume').value = vol;
}
Which (with the caveat noted below) produces:
Going Vertical
Orienting a slider vertically on a web page will occasionally make sense: most users think of volume as “up” and “down”, for example, rather than side-to-side. A reasonable expectation is that using CSS transforms would rotate the element correctly, but that won’t work as expected. Right now, the three major browsers each have a distinct way of making a range
input vertical:
In Firefox, it’s an attribute: orient="vertical"
In Webkit, it’s CSS: -webkit-appearance: slider-vertical
And Microsoft have chosen a rather, um, rather distinct solution: writing-mode: bt-lr;
In this case it’s my hope that the Mozilla approach becomes the standard, but right now orient=vertical
will be flagged during validation. As always, expect things to change in the future.
Combining all three would produce the following:
input.vertical {
-webkit-appearance: slider-vertical;
writing-mode: bt-lr;
}
The HTML:
<input type="range" min="0" max="100" value="50"
class="vertical" orient="vertical">
… and the result you see above.
Styling The Slider
The appearance of the range slider can be customized completely in CSS, using a variety of pseudo-selectors:
input[type=range],
::-moz-range-track,
::-ms-track {
-webkit-appearance: none;
background-color: 3f91e5;
width: 250px;
height:20px;
}
At this point, the slider button can be customized:
::-webkit-slider-thumb,
::-moz-range-thumb,
::-ms-thumb {
-webkit-appearance: none;
background-color: #666;
width: 10px;
height: 20px;
}
With the right selectors, the appearance of the slider can be made equivalent across browsers, as the default – dipping as it does into the UI of the operating system to determine the style of the element – can look very different from one browser to the next.
Generating Tick Marks On The Range Slider
The HTML5 spec allows for a particularly clever feature: linking the element to a datalist
with numeric values via the list
attribute will create a series of ticks along the length of the range bar, with the position of each tick determined by an option
in the datalist
. For example, a variation of our audio control:
<label for="fader">Volume</label>
<input type="range" min="0" max="100" value="50" id="fader"
step="20" list="volsettings">
<datalist id="volsettings">
<option>0</option>
<option>20</option>
<option>40</option>
<option>60</option>
<option>80</option>
<option>100</option>
</datalist>
Combined with the CSS above, creates the following:
In IE, these ticks can be customized by manipulating the appearance of the pseudo-elements ::-ms-ticks-before
and ::-ms-ticks-after
(the areas above and below the slider thumb, respectively). Unfortunately no other browser yet supports this degree of control, and Firefox does not present any ticks at all, at least as of FF 29.
Multiple Slider Buttons
In theory the range
input should be able to take multiple comma-seperated values, generating multiple slider buttons:
<input type="range" value="7,9" min="0" max="10" name="range" multiple>
This would be very useful for indicating upper and and lower range limits, but sadly no browser (as of this writing) supports this feature of the specification. However, Lea Verou has written an excellent little polyfill to gain support for the time being.
Out of The Form
One of the benefits of HTML5 is that <input>
elements no longer have to be associated with <form>
tags, allowing you to place the elements anywhere on a page, not just in forms. This will make the range slider increasingly useful for all kinds of UI controls.
Browser Compatibility & Polyfill Solutions
The range
input is supported in all modern browsers: all versions of Chrome, Safari and Opera; iOS 5+, IE 10+, Firefox 23 and higher.
Making it work in older browsers is usually a matter of applying the right polyfill: html5slider by Frank Yan works very well for Firefox and rangeslider works well across all browsers as a polyfill if you're using JQuery, with many more options are available.
Photograph by Peter Ma, used with permission.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.