Developing an accessible slider

Posted by Scott and Maggie on 03/20/2008

Topics:

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.

Creating accessible "web 2.0" applications can be a tricky process. Many of the advanced widgets and controls we develop today don't exist in the current HTML specification — there is no "slider" or "accordion" or "menu" element, so we must create one from scratch using markup that isn't semantically correct (divs, spans, unordered lists and the like), and layer on the appearance and behavior using CSS and Javascript. This works well when your audience is using a standards-compliant browser, but what about those using older browsers or mobile devices that only partially support the styles and scripts necessary for it to work? And how do you make the fully functional version — a block of non-specific divs and spans — accessible to assistive technology, like screen readers?

The solution we devised is based on the core principle of progressive enhancement: start with basic, functional markup and then layer on complexity, if supported. In most cases, coding for progressive enhancement allows us to build a single code base for both the basic and enhanced versions; we then apply additional CSS and Javascript to create the richer experience for capable browsers. But when we're developing the UI for a web application and creating widgets that capture data, we're often starting with form element markup that doesn't readily cooperate with CSS, or that doesn't have all the moving pieces necessary to create a more complex widget.

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"

select element converted to slider widget

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.

Because there is currently no slider element in HTML, the slider is made up of non-semantic markup (divs, spans), and requires advanced CSS (positioning, background images) and Javascript to work properly. In the absence of CSS or Javascript, the slider is unusable, and the markup is meaningless to users navigating the page with keystrokes or screen readers.

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>

And used it to create a slider when the browser is capable of rendering advanced CSS and Javascript:

Demo page

<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?

We developed the connector script to be smart — it acts on a single container element and detects the number of child select elements. When a single select is found, the script injects a slider with one pull handle for choosing a single value on the continuum. When a second, identical select element is found in the container, the script injects a slider with two pull handles for specifying a range of values as shown in the example below. Users without advanced CSS and Javascript can simply choose the low and high end values from each select, respectively.

Demo page

Our process

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.

Apply enhanced CSS and Javascript, if supported

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.

Demo page

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.

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site

Comments

Righteous slider.  I love the updating tool tips above the slide handle.  That is something that helps the user a lot in understanding their current position.

Comment by Marc Grabanski on 03/21  at  10:24 PM

Very nice work.  I know the aria documentation can lead to a lot of spaghetti code, but if it helps screen-readers and accessibility it can’t be all evil.

I still hope that one day there will be a Javascript accessible flag indicating accessibility needs (sight-impaired, hearing-impaired, etc) that could be used much the same way progressive enhancement currently works only to prevent the rendering of the new widgets.  After all, well marked-up select boxes are already inherently accessible, so if you can detect the need for accessibility and prevent (or revert) the widget’s construction, no problems!

I’ve actually been wondering if I should offer a link to revert something like the slider to the select boxes it was created from.  After all, I know aria documentation exists, but I have no idea how commonly it is used and how intuitive and usable it is to the sight-impaired.

One final note:  I’d make good use of a legend and label for the select box you’re turning into a slider.  Often times you can pass label values forward by referencing them in construction and if Javascript is turned off it makes the code much nicer to everyone, sighted and non-sighted alike.  From seeing your other work, I’m sure you do this and probably just left them out to keep the example code short.

Keep up the awesome work!

Comment by Arthur Lawry on 03/21  at  11:24 PM

@Arthur: Thanks for the nice feedback.
The accessibility flag is an interesting idea. I know we’ve had similar frustration with the limited tools we have for ensuring our widgets can be accessible to assistive devices. Regardless of how we would end up using the flag, I agree that it would be handy to know when a visitor is using an assistive device.

On the other hand, I think ARIA is really inspiring because it lets users with assistive devices actually take advantage of these richer widgets as well instead of being left with a simpler html equivalent (or an inaccessible rich widget). The spec includes UI elements such as tabs, tree structures, live regions and more. As far as browser support goes, it’s already pretty good! Mozilla has info here: http://tinyurl.com/244prh

In the case of our slider, we actually kept the selects in the page ‘behind the scenes’ so devices without ARIA support could still use them. How this can be communicated to the user is something we’re still working out.

Good call on the labels/fieldsets. We agree that in a real application, these would be needed. We noticed our labels were hidden in the third example, which may be what you were referring to. They should be visible now. Thanks!

Comment by Scott (Filament) on 03/22  at  12:29 AM

Very good stuff!
I have an Issue using Keyboard only though.
In all the Examples on this Page I am only able to move the second Handle by using the Arrow Keys. The first one does never react (Firefox 2 OS X).

Comment by Dirk Ginader on 03/26  at  04:16 PM

@Dirk:  Thanks for the comment.  We’re planning to update this slider to work with the latest jQuery scripts (including the newer UI library in place of Interface), and when we do that one of our top priorities is to make sure key events work on both handles.  We’ll post an update when we’ve got a working version.

Comment by Maggie (Filament) on 03/26  at  04:35 PM

This is great! We’re currently working on a project that requires sliders for a custom window blinds building tool. We are using jQuery’s interface slider, but never thought of taking this approach. Thanks!

Comment by Blake Bauman on 03/26  at  05:48 PM

You guys probably already no about: http://groups.google.com/group/jquery-a11y

If not, please join!

Comment by David Bolter on 03/27  at  03:24 PM

s/no/know sigh… yes I’m a programmer.

Comment by David Bolter on 03/27  at  03:25 PM

Great work, just a small but annoying glitch for me the slider doesn’t work with the keyboard the slider take the focus but when I try to use the arrow key I have a javascript error : this.dragElem has no properties (Firefox 2.0.0.13 win XP)

Comment by goetsu on 03/29  at  07:56 PM

This thinking is really quite interesting. Is your “connector scritpt” proprietary, or would you be willing to share it as a guide to aspiring developers?

Comment by Garlin Gilchrist II on 04/02  at  08:47 PM

@Garlin—Thanks for the comment.

The code posted here is open source (our scripts are licensed under GPL), so you’re welcome to view source, look through the scripts, and use them if you’re OK with taking them “as is.” (We’re currently working on an updated version of the slider and plan to phase out this one.) Just keep in mind that the connector script shown here works solely on select elements to display them as slider widgets. 

Here’s a shortcut to one of the samples (view source to grab code):
http://www.filamentgroup.com/examples/slider/index2.php

Comment by Maggie (Filament) on 04/03  at  12:22 AM

I noticed in the example of two related sliders (capturing a range) that the default behavior that I would expect (slider position increments the same as the visual scale, so 4:00pm would appear to have the slider arrow in line with the scale line) didn’t seem to be how you chose to implement it.

Is this a choice or a bug and I’m just wondering what caused the decision if it was a choice.  I got confused when I was at 4:00pm, dragged the slider right and saw the slider leap to… 4:00pm!

In case this is a browser/platform specific bug, I’m viewing the page with Firefox 2.0.0.13 on Windows XP.

I hesitated to comment as I know you are well into development for a new version of the slider.

Comment by Arthur Lawry on 04/08  at  07:31 PM

Hi Maggie, Superb idea this slider is. As far as updating this slider for the new UI lib, are you going to wait for the UI lib to be bug free? In other words, how far are you converting this slider to the new UI code?

Comment by Gilles on 04/14  at  10:37 AM

I was looking for such slider and found it ! ;-)
thx for your post. gonna book your site.

regards!

Comment by decimus on 04/14  at  04:15 PM

I’ve noticed a bug: if we move the right marker to the right end and then overlap it with the left marker we can’t move anything. Any attempt choose the right marker but it can’t be moved nor to the right (because it is the right end), neither to the left (because the left marker is here). And it is no way to choose the left marker to move it and free the situation.
I’m using Firefox 2.0.0.13 via Windows XP

Thank you

Comment by sunnybear on 04/16  at  07:46 AM

What a nice script! Looked around for some alike. Thank you for your work!

Comment by prank ideas on 04/16  at  11:13 AM

re: sunnybear’s bug - I would think you could fix this by switching each component’s z-index as you get to the corresponding end (right end = left on top, left end = right on top) although another solution would be to make your sliders “half-sliders” where the left slider looks like the left half of a slider and the right slider looks like the right half of a slider.  That way both controls can occupy the same space while both being accessible.  To me, that’s important when you set both sliders to the same mid-range value.  I want to be able to drag whichever one I want without worrying about which one is on top.

Comment by Arthur Lawry on 04/16  at  12:51 PM

really its cool, can any one explain me how i can implemet it in my project. Just i want to display persons name in that age group..

Comment by ravi on 04/16  at  02:33 PM

It’s not smooth on IE6 ...=/

Comment by Acronyms on 04/17  at  02:14 PM

nice work :)
Keep it up

Comment by fedmich on 04/20  at  10:22 PM

Comment by RedesignYourBiz.com on 04/29  at  06:06 PM

Very good job, AJAX forewa!!!

Comment by pergam on 05/05  at  11:05 AM

Can the slider be incorporated into a mulit select input therefor have multiple selection sliders.  So with your example above someone can select 9am-Noon and 3pm-6pm.  this would multiselect the one input options.

the select contains the 9-10, 10-11,11-noon,noon-1,1-2.,..... with the value being the first hour in the text 9-10 value = 9

then the form posting will have the name of the select time[] = array (9,10,11,3,4,5) as the selected options.

double click adds new selector? When on selected over laps the other the tow merge to form the one select range. click within the select range creates two more slide one to end the fist and one to start the second range. etc.

Your api is like Hijax but for gui - very nice.

Comment by Reuben on 05/06  at  01:11 AM

I found it bit a difficult scrapping it from your example there were a few things hidden - give us a break, PACKAGE IT UP!!!

Comment by Greg on 05/08  at  06:57 AM

Hi, Scott and Maggie.

Thanks for the useful example.

We have recently launched web-service that allows to compare loose diamond prices. It has several sliders on the homepage. Take a look: http://www.diamondpriceguru.com/

And here is a prototype draft of the next upcoming version: http://screencast.com/t/6Unfu79T Do you find new interface more visual, informative and usable?

Comment by Sergey Kapustin on 05/08  at  09:12 AM

I tried viewing source and copy/pasting all the scripts and what not, cant seem to get it to work :(

Comment by Jack on 05/13  at  10:06 PM

@Greg and Jack:  In this article we set out to describe a way of thinking about building accessible widgets and used the slider as an example. Because the code examples shown here use older scripts that have known issues and need improvement (see the Really important side note above) we didn’t package them for quick download.  If you’d like to use them, you’re welcome to do so “as is” and debug as needed.

For those of you interested in a reusable slider, stay posted:  we’re planning to release a new slider based on the recently updated jQuery UI library.

Comment by Maggie (Filament) on 05/13  at  11:20 PM

how can i download it

Comment by Zaid Rousan on 05/19  at  10:33 AM

how can i download this

Comment by Zaid Rousan on 05/19  at  10:33 AM

Hello!
For me this one is working very well!
Only ‘bug’:
When I set the slider to show all values and set it to a wider range, the labels for the individual ticks do not get aligned at the right positions: $(’#slider1’).makeSlider(’’, 260);
See what I mean here: http://img90.imageshack.us/my.php?image=sliderei4.jpg

Comment by tronics on 05/20  at  01:05 AM

The select item, does not work Ok in Opera (9.26)

Comment by Daniel G. Blázquez on 05/22  at  07:19 PM

It works fine if the container is initially displayed but when the container has the class display:none and is displayed later all spans are getting a negative left attributes and the slider is squashed. When the slider is initialised the sliderComponent.width() = 0 when the container is hidden. and and in my case 629px when the container is initialy not hidden.
created code:
<span style="left: 0pt;">
<span style="left: -3.6px;">
<span style="left: -7.2px;">
<span style="left: -10.8px;">
<span style="left: -14.4px;">
<span style="left: -18px;">
<span style="left: -21.6px;">

Comment by Ron Jonk on 05/26  at  03:34 PM

Looked some further..after the function call: sliderComponent.width(sliderWidth);
the width stays 0 when width is given a value.

Comment by Ron Jonk on 05/26  at  03:52 PM

how close are you guys to the next version? im very impatient :P

Comment by Jack on 06/02  at  07:30 AM

How can i download the source code?Plz help me reg this..

Comment by Govardhan on 06/05  at  12:42 PM

I had a discussion about this very concept with Alex Russel (of Dojo) at Rich Web Experience last year (albeit with a single SELECT, not multiple), so I’m ecstatic to see an implementation of this.  I think there is a whole series of progressive enhancement design patterns out there to be created and I have plans to begin assembling them as guideposts for widget libraries and individuals to build against. I tried to get it off the ground within WaSP before Jeremy shut down the DOM Scripting Task Force, but that didn’t happen. I’m still eager to have a go of it though. Would you guys be interested in participating in such a project?

Comment by Aaron Gustafson on 06/05  at  01:56 PM

@Aaron: Thanks for contacting us. We’re excited by your interest in this technique and would love to talk with you about your ideas for further implementations. We’ve applied a similar pattern in several other cases which have yet to make it onto our lab, such as using the same markup to create tabs or accordions, and other ARIA component scenarios. And there are some other examples we’ve written about on here as well such as building charts from table element data. We’re also working very closely with the jQuery UI team on their set of widgets (from a design perspective anyway), and we’d be really excited to see more mainstream adoption of P.E. patterns like this in that and other JS libraries.
Please drop us an email at and we’ll look forward to hearing from you.

Comment by Scott (Filament) on 06/05  at  03:43 PM

Thanks Scott, I’ll be in touch shortly.

Comment by Aaron M Gustafson on 06/10  at  04:52 PM

does not accurately
and not quite correctly handles overlap sliders

more correct slider, with great potential
http://habrahabr.ru/blog/javascript/41892.html

Comment by guest on 07/09  at  02:39 PM

Buenazo, uno de las mejores paginas que brinda, buenos recursos, al programador y analista de sistemas

Comment by Eddy Rosales on 07/19  at  02:01 AM

have the sense to package it up at least.... this is a pain to go through all your source and manually do this. great contribution, but you really need to package it when you’re finished

Comment by jt on 08/05  at  05:22 AM

@jt (and others looking for a quick download):  Thanks for the interest in our code, we’re glad you found it worthy.  :) We didn’t package it up on purpose. As we mentioned in the post and in an earlier comment, this our first proof-of-concept based on the jQuery UI slider (http://docs.jquery.com/UI) and it’s not production-ready.  Our goal here is to share our thoughts on this ‘API’ approach with the dev community to get a conversation going, which seems to have worked well as we’ve gotten some interesting and helpful feedback.  We’re continuing to refine this widget and others, and we’ll post download-ready code when solid versions are ready.  Until then, you’re welcome to mine the demo pages for code to use “as is” with proper attribution, with the understanding that it will take some work on your part to make it bullet-proof.

Comment by Maggie (Filament) on 08/05  at  05:51 PM

using this slider it is possible to update the values when we slide? can I rewrite the slide function ??

Comment by tina on 09/17  at  08:05 AM

can you send me the example?
thanks you !

Comment by jooben on 09/28  at  09:01 AM

thanks,

Comment by tadaee on 10/18  at  02:10 PM

I am trying to make a multiple slider (more handles on a slider).
N handles on a slider.
Thanks

Comment by Kinny on 10/22  at  09:38 AM

hello good idea ! :D

Comment by cmoi68 on 11/02  at  04:40 PM

just a thought here, but this would be even faster if it simply detected where a mousedrag begins and ends. The user, wanting to select a time period between 10am and 4pm, would in effect draw a line with the left mouse button between these values, which would select from one to the other, much like how one might select adjacent cells in a spreadsheet.

Comment by Rory on 11/26  at  07:24 PM

Hi, i wonder if there is a leightweight version of the slider (with 2 handles). I think it is a lot overhead to use alle the js just to use the slider?
Anybody any idea?
Thanks

Comment by Vivienda on 01/02  at  05:13 PM

@Vivienda: You can just use jQuery UI’s slider on its own if you want less code weight. This purpose of this plugin is to build a slider using progressive enhancement so that users with less-capable devices (or those with javascript disabled) would still be able to use the control as an HTML select element. Regardless, we highly recommend that you use a newer version of the slider plugin.

This plugin has been updated to jQuery UI 1.6 and we’ve posted a new article detailing its features. You can find it here:
http://www.filamentgroup.com/lab/update_jquery_ui_16_slider_from_a_select_element/

Comment by Scott (Filament) on 01/16  at  01:26 AM

Bug. Playing with that slider found a bug, i’ve moved both idicators to 8pm, then i couldnt do anything else.

Comment by Marcin on 02/18  at  05:39 PM

Excelente herramienta

Comment by Heinrich Sanchez on 03/15  at  05:13 AM

this is excellent

Comment by subodh on 03/30  at  09:24 AM

This is just what I was looking for.  Frankly though - I can’t believe it’s still necessary (:  Come on browser developers and get it together already.  The web has been around for well over a decade and still JS code is ‘write once and debug everywhere’ (I’m glad to be a systems developer.  jquery looks pretty cool, but the browser incompatibility issue is ridiculous.)

Comment by lynn on 04/22  at  04:05 AM

tnk you its very good

Comment by thnk on 06/10  at  03:21 AM

Very well done - love the blend of progressive enhancement and incorporation of ARIA specs to create this accessible tool.

Comment by Marty DeAngelo on 06/16  at  06:02 PM

Your slider looks great. Can I have the js codes and css files for your slider ? thanks

Comment by DINH DIEP on 08/22  at  02:21 PM

How should i create vertical slider using this?

Comment by Amit Shah on 09/05  at  11:32 AM

This is very helpful idea, we appreciate this.

Comment by Web development lucknow on 09/15  at  12:00 PM

How should I place two images on single slider?
Which can be use for minimum-maximum slider.
eg. >> image on left side of slider and << image on right side of slider

Comment by Amit shah on 09/15  at  12:11 PM

Thank you for posting and share this nice post with us…

Comment by Ajax Developers on 09/22  at  12:11 PM

How can we prevent overlapping of two slide images on single slider?

Comment by Amit shah on 09/22  at  12:17 PM

Thank you for useful example.
Has somebody use this slider as a timeline? dragging a range of values horizontally or just moving horizontally by sliders?

Comment by Alv on 11/11  at  06:05 PM

I’ve fixed a bug with UI 1.7.2
_________________________________
var feedback = jQuery(ui.handle).find(’.ui-slider-tooltip’); // ### fix by Dakkar
feedback.html(currText).parent().attr(’aria-valuetext’, currValue).attr(’aria-valuenow’, currValue);
//control original select menu
var currSelect = jQuery(’#’+jQuery(ui.handle).attr(’id’).split(’handle_’)[1]); // ### fix by Dakkar

Comment by Dakkar on 11/24  at  07:20 PM

Hello,
The work you had done is really appreciable ,but you had not provided the link to download the code.If you provide the code also than it will be very helpful for the developers while working.

Thanks

Comment by sweta on 12/24  at  04:38 PM

How should we create vertical slider using this?

Comment by infrared on 03/07  at  03:13 PM

Commenting is closed for this post.

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site