Building Accessible Widgets for the Web
Got something to say?
Share your comments on this topic with other web professionals
In: Articles
Published on July 10, 2006
When building web applications, we’re working with a fairly restricted set of widgets, compared to those available for native desktop apps. The recent surge of interest in JavaScript points to a solution: We can replicate the functionality of sophisticated native widgets through some clever DOM scripting, and provide our users with exciting interfaces that bridge the gap between native apps and web apps.
This approach works wonders, and our interfaces can indeed be substantially improved through scripting. It’s critical, however, to ensure that we bring all our potential users with us as we work around HTML’s limitations. Building exciting interface behaviors is completely wrongheaded if doing so reduces accessibility. The interfaces we build ought to provide the same central functionality to all users, period.
Happily, there’s a straightforward way to ensure that a JavaScript-enhanced interface works correctly for even the least capable browser: build the interface in its most basic incarnation first. Before jumping ahead to the hi-fi widgets you’ve always dreamed of, think lo-fi. Dig into the core functionality you want to provide, and map it to the basic HTML elements you know you’ll always have available to you. You can keep your forms accessible—while significantly enhancing the experience for users of modern browsers—if you serve up a lo-fi form, and then unobtrusively transform it into an exciting hi-fi form “onload.”
Think Lo-Fi First
A combo box is a great example of a native widget that we must build from scratch for the web. Let’s apply the lo-fi principle, and talk about how we might create a lo-fi combo box that provides the basic, no-frills functionality we’re looking for.
The central question is, “What does a combo box do?” At its core, a combo box is simply a drop-down list with the added ability to accept a user-entered value instead of the preset options. There are some other interesting UI bits like type-ahead search, but it’s really just a SELECT list with an INPUT text field glued on. If we want to provide users with the ability to pick values out of a list or enter their own free-form values, then we can implement the purely semantic meaning using those basic elements:
<fieldset class='combo_box'>
<legend>Combo Box Widget</legend>
<label for='select_value'>
Select a value:
<select name='select_value' id='select_value'>
<option value=''></option>
<option value='1'>lorem</option>
<option value='2'>ipsum</option>
<option value='3'>dolor</option>
<option value='4'>sit</option>
<option value='5'>amet</option>
</select>
</label>
<label for='input_value'>
Or, input your own:
<input type='text' name='input_value' id='input_value' />
</label>
</fieldset>
Simple, eh? We’ve grouped the INPUT and SELECT elements (and, of course, their LABELs) in a FIELDSET of class combo_box
to signal their relationship to one another. This code distills the combo box down to its core components, and the result is basic enough that we know we’ve provided the desired functionality to anyone who uses the form. We can validate the input server-side if necessary, and create a seamlessly accessible interface for all users.
If we were creating a calendar control, we could start in exactly the same way. The core functionality of a calendar is the ability to pick an arbitrary day, month, and year. This maps cleanly to three SELECT elements, perhaps with the following structure:
<fieldset class='calendar'>
<legend>Calendar Widget</legend>
<label for='year'>
Year:
<select name='year' id='year'>
<option value=''></option>
<option value='1900'>1900</option>
...
<option value='2006'>2006</option>
</select>
</label>
<label for='month'>
Month:
<select name='month' id='month'>
<option value=''></option>
<option value='1'>01 – January</option>
...
<option value='12'>12 – December</option>
</select>
</label>
<label for='day'>
Day:
<select name='day' id='day'>
<option value=''></option>
<option value='1'>01</option>
...
<option value='31'>31</option>
</select>
</label>
</fieldset>
The principle here is the same. We distill the core characteristics of the widget we’re creating, and then come up with a clean HTML isomorphism. With—and only with—that accessible foundation in place, we can start putting together the additional functionality for our hi-fi users.
Wireframe a Hi-Fi HTML Framework
JavaScript gives us the freedom to completely rework the DOM on the fly. This means that we needn’t feel at all limited by the lo-fi structure when creating our hi-fi interface. We can reshape the lo-fi HTML any way we wish in order to achieve the UI behavior that we’re looking for, so let’s continue with the combo box example by being specific about what that behavior is.
After playing around with the native combo box we’re trying to replicate, we might come up with this list of features:
- A toggle button at the right edge of the text-entry INPUT field will show or hide the drop-down list when clicked.
- Typing a value into the combo box’s text-entry field will show the drop-down list if it’s currently hidden, and the value that best matches that text will be highlighted.
- Pressing the up or down arrow keys while focused on the text-entry field will move the selector up or down in the drop-down list, and will populate the text-entry field with the newly selected value.
- Clicking on a value in the drop-down list will mark it as selected, populate the text-entry field with the newly selected value, and hide the drop-down list.
- Pressing Enter or Esc will hide the drop-down list.
We can divide this list fairly easily into behavioral and structural additions. We’ll set up various event handlers to handle the former (e.g. the Enter keystroke can be captured by hooking into an onkeyup
event), and the latter will require modifications to our framework (e.g. we need a toggle button of some sort). These structural additions will be our first concern; event handlers aren’t much use without a solid structural framework to hang them on.
As it turns out, we can build the hi-fi combo box’s structure on the basic components of the lo-fi version (remember, it’s just a SELECT element and an INPUT element). The INPUT element is exactly what we need for the combo box’s text-entry field, and we can hide and show the SELECT element to replicate the combo box’s list of options. We’ll need to add a button of some sort (perhaps an INPUT of type image
so that we can have a nice-looking arrow), and we should give the SELECT a size
attribute so that multiple OPTIONs can be displayed at once, in keeping with the native widget’s appearance. The HTML code that reflects these changes might look something like this:
<fieldset class='combo_box'>
<legend>Combo Box</legend>
<label for='input_value'>
<!-- The SELECT’s LABEL -->
Select a value:
<!– The original INPUT –>
<input type=’text’ name=’input_value’ id=’input_value’ />
<!– A new toggle button –>
<input type=’image’ src=’/files/includes/images/path-to-image.png’ />
<!– The original SELECT –>
<select name=’select_value’ id=’select_value’ size=’4′>
<option value=’1′>lorem</option>
<option value=’2′>ipsum</option>
<option value=’3′>dolor</option>
<option value=’4′>sit</option>
<option value=’5′>amet</option>
</select>
</label>
</fieldset>
With this new framework in mind, let’s discuss the behavioral modifications. The text-entry INPUT field needs a handler bound to the onkeyup
event to enable the type-ahead search, the up and down arrow keys, and the enter/escape keys. The toggle INPUT button needs an onclick
handler to support the hiding and showing of the SELECT list, and the SELECT needs an onclick
handler to support the selection process we decided on.
See the demo for a working example. The technical details of ComboBox
’s implementation are laid out in the comments of the ComboBox.js script, so those of you who are interested can dive right in. The remainder of this article will deal with the more general question of how that transformative code gets executed.
Transform Unobtrusively
We could certainly throw the relevant DOM manipulation into a SCRIPT block after each FIELDSET, but a cleaner and less obtrusive option exists. In the same way that external stylesheets provide the ability to strictly separate the semantic layer (HTML) from the presentational layer (CSS), we can separate the behavioral layer from everything else by creating a JavaScript object that runs when the page’s onload
event triggers. We can throw the object’s definition and instantiation into an external file, meaning that a single SCRIPT tag at the top of our document can kick off the entire process.
The minimal frame for our object looks like this:
function ComboBox () {
var self = this;
self.instantiate = function () {
// Interesting initialization code goes here
}
addEvent(window, 'load', self.instantiate);
}
var ComboBoxFactory = new ComboBox();
This code isn’t complex, but deserves careful attention. We’re creating a closure by defining a function named ComboBox
, and setting up various methods and attributes inside.
The first line sets up self
as an alias for this
in order to avoid some scoping issues later on. Specifically, we’ll be able to set up event handlers inside our main closure that use the keyword this
to refer to the event’s target (the INPUT or SELECT elements), and self
to refer to the ComboBox
object itself. For example, here’s the onclick
handler for the SELECT element:
self.selectClickHandler = function(e) {
/*
If we click on the SELECT, set the INPUT to the text
value of the selected OPTION, hide the SELECT, and
focus on the input box.
*/
var container = this.parentNode.parentNode;
container.input.value = this.options[this.selectedIndex].text;
this.style.display = 'none';
container.input.focus();
return false;
}
The event handler is triggered when the user clicks on the SELECT element, meaning that the function is bound to that element’s context. this
, even in an event handler defined inside our closure, can be used to refer to the SELECT element without confusion if we consistently use self
to refer to the closure itself.
The call to addEvent
on the last line of the ComboBox
function is critical, as it allows us to delay execution of the real innards of our object until the window’s onload
event triggers. Let’s examine the step-by-step execution of the closure to make sure that everything’s clear:
- When the .js file is loaded, the closure is defined.
- The
new
operator on the last line of the file instantiates a new object, executing the code inside the closure.self
is created as an alias tothis
.instantiate
is created as a method ofself
, and assigned a function.- The
addEvent
call on the last line of the closure is executed, bindingself.instantiate
to the window’sonload
event.
- The page finishes loading, and the
onload
event is triggered. - The
instantiate
method of ourComboBox
object is executed, transforming all the lo-fi combo boxes on the page into beautiful hi-fi versions.
This procedure makes JavaScript modifications as easy to implement as CSS. You can build a series of reusable, interface-enhancing JavaScript utilities, and reap the benefits with one simple SCRIPT tag to kick off the entire process. Combo boxes are just the tip of the iceberg.
Additional Reading
- Ryan Campbell’s article Upgrade your SELECT Element to a Combo Box provided the initial inspiration for this article’s example code. I prefer my mechanism to his, simply because his doesn’t provide the same core functionality to users without JavaScript.
- Simon Willison’s article Closures and executing JavaScript on page load is valuable reading on the subject of closures in general.
- Chris Heilmann’s Unobtrusive Javascript is a wonderful introduction to the topic.
Related Topics: Accessibility, Scripting, XHTML
Mike West abandoned suburban Texas’ wide open plains in 2005 in favour of the Black Forest in Southern Germany where he currently lives and works. His musings about the web are periodically posted to his personal website, mikewest.org.