Developing an accessible slider
Posted by Scott and Maggie on 03/20/2008
Note: We've created a new article with some major improvements to this technique. To read the article and download the script, visit Update: jQuery UI Slider from a Select Element - now with ARIA Support. To read about the original logic behind this approach, please continue reading here.
To solve this problem, we developed an "element API" — a script that combs a form element for relevant information (data values, key structural characteristics), and uses that information to build a new widget on the fly and insert it into the DOM. Below we describe a test case that outlines our process for making this work.
An "element API"
Recently we designed and developed an interface that required a slider control, which allows users to choose one or a range of values on a continuum. Values on a slider can represent anything from hours on a clock to the volume on a music player to a complex, proprietary data set. In its simplest form, the slider is displayed as an axis of values with a handle to drag and select a value, or two handles for selecting a range.
For the slider to be usable in the absence of styles and scripts, we decided that alternate markup is necessary — rather, we applied the principals of progressive enhancement and looked for an analogous HTML form element that could do the job on its own, something with similar features and functionality that:
- supports selecting a single value, or a range,
- provides feedback on the value selected, and
- allows for a default value to be set.
We chose the <select> form control because, like sliders, it natively performs the functions listed above, and also comes with the added bonus of grouping multiple options (with the <optgroup> tag) and clean management of large data sets. Then, to make the slider we created a script — an element API, or connector script — that leveraged the structure, data values, and labels in the standard select element. The select is served to all browsers, and if the browser can support the slider's advanced styles and scripts, it's hidden and a slider is inserted into the page.
So we took a standard select element:
<select name="speed"> <option value="Slower">Slower</option> <option value="Slow">Slow</option> <option value="Med">Med</option> <option value="Fast">Fast</option> <option value="Faster">Faster</option> </select>
<div class="sliderControl"> <ol class="xScale clearfix"> <li class="Slower"><span>Slower</span></li> <li class="Slow"><span>Slow</span></li> <li class="Med"><span>Med</span></li> <li class="Fast"><span>Fast</span></li> <li class="Faster"><span>Faster</span></li> </ol> <div class="scale"> <span style="left: 0pt;"/> <span style="left: 31px;"/> <span style="left: 62px;"/> <span style="left: 93px;"/> <span style="left: 124px;"/> </div> <div aria-valuemax="Faster" aria-valuemin="Slower" role="slider" class="indicator1 sliderIndicator" tabindex="1" aria-valuetext="Slower" aria-valuenow="Slower"> <span class="indicatorFeedback1">Slower</span> </div> </div>
Need to capture a range of values?
Start with solid markup
When writing the markup for the select control, we paid particular attention to how the select's standard child elements, values, and text map to similar elements in the slider:
- the number of options (<option>) is used to calculate the scale and distance between labels on the slider axis
- option selected attributes (selected="selected") are translated into default values on the slider
- option value attributes (value="abc") provide feedback in the form of a "tooltip" that sits above each slider handle
- option text nodes (<option>Text</option>) become the labels along the slider axis
- optgroup elements (<optgroup>) are used for dividing the axis into regions, like AM and PM, and
- optgroup label attributes (<optgroup label="AM">) are used to identify the axis regions.
As the page loads, we test the browser to see if it's capable of rendering advanced styles and scripts correctly. If it fails the test, nothing happens — the browser renders the simple, fully functional markup it started with. But if it passes, the test script applies advanced styles and scripts for a richer, more interactive experience. That's when we use the script to hide the select and insert the slider. (We describe how we test a browser's capabilities and provide an enhanced experience in Delivering the right experience to the right device.)
Really important side note: we wrote the connector script — a plugin based on an older version of the jQuery library — that specifically builds a slider from a select element. The script that runs the slider is part of the Interface UI library, also based on jQuery. If you'd like to reproduce this code, we strongly recommend that you grab the latest version of jQuery and create a new slider — a good starting point may be the updated slider in jQuery's UI library. You're welcome to use our connector script as a model, just keep in mind that it will need to be updated and edited for production-ready code.
Keep it accessible for all users
The slider markup is injected into the DOM immediately after the select element, and the select is hidden visually (it's still in the source). Events assigned through the DOM allow the user to manipulate the slider, which in turn updates values in the hidden select element so that the correct select option is submitted with the form. And since the select element is still technically on the page, users with assistive devices can use it in place of the slider.
We've also added attributes to the slider component as specified in the WC3's ARIA spec. ARIA attributes provide another layer of accessibility because they allow developers to declare the role of each non-semantic widget. Browsers such as IE8 and Firefox 3 have begun to support these features, so we're excited to begin using them in our applications.
A slider can be made ARIA-accessible by adding the following attributes to the slider handle (which in this case is a div element):
- tabindex allows the element to receive keyboard focus
- role="slider" makes the assistive device aware of the slider control
- aria-minvalue is the minimum value in the slider scale
- aria-maxvalue is the maximum value in the slider scale
- aria-valuenow is the currently selected value
- aria-valuetext is a user-friendly equivalent of the currently selected value
The markup with ARIA attributes:
<div role="slider" aria-valuemin="6am" aria-valuemax="8pm" aria-valuetext="11am" aria-valuenow="11am" tabindex="1"&t;</div>
The slider examples shown here are ARIA-accessible, and currently some key events are in but need more work to be fully functional. Making the key events work correctly is something we consider to be a necessity, so it's something we'll continue to refine.
We're always looking for ways to improve our code and its accessibility — in particular, we're working on ways to make the "API" more generic — so If you have any ideas about how to fine tune this concept, please drop us a line.