Accessible Ajax: OmniTI

PHP all-stars, OmniTI, recently launched their web site; Jon Tan and I had the pleasure of aiding in its creation.

You can read more about the site’s development on Jon’s blog, starting out with evolving OmniTI’s brand with their team through to a review of the site’s build. Here I’m going to show you what we did to enhance the site with some JavaScript functionality and how we ensured that content updated using Ajax was as accessible as possible.

Progressive enhancement

Progressive enhancement firmly in mind, we wanted to provide users immediate access to some useful information, such as contact details and the search form. The JavaScript functionality is strictly separated from the structured content of the site into external files – no scripting in the markup.

The contact details and search form have dedicated pages on the site, which serve as fall-backs for cases where JavaScript is unavailable. When JavaScript is disabled, the search and contact links will take you to the respective pages. If JavaScript is enabled, it runs when the page loads and sets up everything that it needs for the added functionality.

We opted to use the jQuery JavaScript framework to implement these features. Using a framework allows you to quickly build prototype features and makes code easier to maintain in the long run. We specifically chose jQuery because it is fast and lightweight.

Sensible Ajax

The additional layers of markup for both the contact details and search layers are requested from the server using Ajax, which jQuery makes really easy. It’s common for such layers to be embedded in the page markup and hidden using CSS when the page loads. The information in these layers are for convenience and is available on other pages of the site, so it doesn’t need to be on every page. Using Ajax in this way also allows the markup for those layers to be easily maintain server-side.

Having requested the markup as necessary using Ajax, it may be tempting to inject the markup into the DOM straight away. If you do this and disable CSS, the markup will be visible at the top of each page, just as it would if you had embedded it in every page on the site. It just doesn’t need to be there. Instead, the markup is held in a JavaScript variable rather than injected into the page from the start. These layers are only added to the page when they are needed. This way, even if CSS is disabled, the content is only there when it needs to be and doesn’t interfere with the rest of the content.

The interface

When a visitor hits either the contact or search links at the top of every page, if JavaScript is enabled, the appropriate layer of information slides out from the top of the page. Hitting the close button or the link that activated the layer removes the layer from the DOM rather than simply hiding it with CSS. As a nice extra, hitting the contact link when the search layer is open will toggle between the two layers.

That’s all fine for the majority of users. Let’s make sure we think about other users who may have different, and diverse, needs.

A more accessible interface

Sighted users see the lovely jQuery slide down effect when they click one of the activating links; it grabs their attention and shows them that new information is on the screen. If you can’t see, you need different feedback to tell you what’s happened when you followed the link, otherwise you might think that nothing has happened.

For keyboard users, which includes most screen reader users, we need to give the added layer focus in some way. JavaScript to the rescue; we can move keyboard focus to a focusable element using some simple code. If we don’t do this, users will have to navigate a long way through the page to get to the place that the new layer has been added to the DOM. As you might imagine, few users will be patient enough to search around for the new information. In fact, they may not even know that new information is on the page.

Sometimes it will make sense to inject new information into the DOM just after the element that actions the addition. Doing so will help screen reader users to find the information because the very next thing they read is likely to be that new information. However, for the OmniTI site we wanted to add the new layers to the top of our page for visual effect. So, we need to tell JavaScript to focus on something appropriate within the new layer.

Using focus for feedback

We need to pick a focusable element in the new layer we’ve added to the page, i.e. a link or a form element. Conveniently – although, some might say by design – there are appropriate elements at the start of our new layers. In the search layer, it makes sense to focus on the search input of the form. In the contact layer, the first element is an email link, so we can put focus on that.

Keyboard focus can be brought about easily by calling focus() on the required element, so long as the element is focusable. For example, after injecting the layer, we can pick it out the target element and give it focus:

document.getElementById('myLink').focus();

Ironing out some creases

During testing, the most prevalent screen reader software, JAWS, coped well when focusing on form elements. However, it doesn’t always seem to like focussing on links; the screen reader’s buffer (its copy of the page’s DOM) seems to get updated correctly, but its virtual cursor is not always moved when focus() is called in the JavaScript.

Where Ajax is involved, problems can occur when the screen reader updates its virtual buffer before the Ajax call is complete. The response to the Ajax call may result in a change in the browser, but that change is not passed on to the screen reader. As our Ajax calls are only executed when the script is initiated, that’s unlikely to be the case this time.

As a focusable element, the target link should be able to receive focus with no trouble, but in conjunction with injecting elements into the DOM, it doesn’t always work. However, there’s a fix.

Technically, setting a tabindex value of -1 on an element should allow any type of element to become focusable. (You can read more in Gez Lemon’s Making Ajax Work with Screen Readers.) Obviously, the link should be focusable anyway, but using JavaScript to set a tabindex value of -1 on our link before we give it focus results in more reliable behaviour from JAWS.

Update: I found out from Christian Heilmann that this is likely to be a symptom of Internet Explorer’s hasLayout issues, which should mean that it can be fixed by triggering hasLayout on the target element.

We’ve got one last problem to iron out. In some browsers, setting a tabindex value of less than zero will remove the element from the tab order of the page. Keyboard users will often make use of the Tab to navigate around the focusable elements of a page. The tab order is the sequence in which these elements are given focus as a user tabs around a page. As our link should be in that tab order by default, we’ve just removed it by messing with its tabindex. Setting the tabindex value to zero (and possibly blank, but I’d need to double check) will put the link back in its place in the tab order. I don’t recommend setting tabindex in markup, but if you happen to be doing so, you should set the tabindex back to its expected value.