Photograph of the bare Saskatchewan plains, under a blue sky

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 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 the width 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:

50%

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 to the right.

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 specification feature.

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.