case study

JavaScript and screen readers

In recent months, many web designers have been asking the question, “How can I make my dynamic site accessible?” Unfortunately, good answers have been slow in coming from the accessibility community. While some articles have touched on the problem, not many have gone into details. Here is our first attempt at rectifying this situation.

Why is it so difficult for us to come up with some simple recommendations like we did for web 1.0?

A large part of the problem stems from the complex nature of JavaScript. There are so many things to do with it, and so many ways to do them, that making blanket statements about what works and what doesn’t becomes a challenge. For example, screen readers seem to vary widely in how they support and react to JavaScript events. Nevertheless, if we are to have any hope of ever solving this problem, we have to start somewhere. Hence, this article.

In order to keep this article the size of an article, and not a book, we will only be considering how screen readers interact with various JavaScript events. Screen readers are complex beasts, and understanding why they do what they do can be quite a challenge. We have attempted to explain these concepts as clearly as possible, but you should spend some quality time getting to know screen readers such as Jaws or Window Eyes, or perhaps both.

This article is by no means the final word. We are just beginning to scratch the surface. Much more research is needed.

How Screen Readers Read Web Pages

To understand why screen readers have problems with JavaScript in the first place, we have to understand how screen readers read web pages.

The name screen reader is a misnomer since they do not read the screen. Screen readers load the page content from the browser’s document object model (DOM) into a virtual buffer. The user is then able to review this virtual buffer at will, using numerous hotkeys. Items such as links, form fields, and some other elements are identified when they are read. When the user finds a link or other clickable element that they wish to activate, they simply press Enter, and the screen reader tells the browser where they “clicked”.

Form controls are handled differently. When a user encounters a form control they wish to manipulate, they press Enter, causing the screen reader to enter Forms Mode. When in forms mode, the user is only able to “see” the form control that has focus. They cannot read the page content until they leave Forms Mode and return to Virtual Mode. The user can press tab and shift+tab to iterate through the form controls, and, depending on the control type, use the arrow keys, or enter text to modify the control that has focus. Note however, that just because a particular control has focus in the virtual buffer, it does not necessarily have it in the browser. (This last point will hopefully become clearer in a moment.)

Earlier versions of screen readers would sometimes show the user a page that was out of date, because the page would change, but the virtual buffer wouldn’t get updated. Fortunately, this problem seems to have been resolved in more recent versions, and the virtual buffer is refreshed from the DOM every few seconds.

Alerting the User

When a page refreshes in the browser, or when a new page is loaded, the user usually knows it because the screen reader announces it. However, if JavaScript simply updates the DOM, Jaws remains silent. The user may never realize that something is different. This can pose quite a challenge. How can you notify the user not only that there is a new piece of information, but also where they can find it? A sighted user would probably see that something new popped up, but a screen reader only reads what the user tells it to.

For instance, if the user clicked a help button, and JavaScript dynamically displayed some text, how would the user know it? The screen reader probably won’t say anything. What’s more, even if the user did know it was there, they wouldn’t know where it had popped up within the virtual buffer.

Fortunately, the situation described above isn’t totally hopeless. The user has clicked something, so they are probably expecting something to have changed. Whether their screen reader says anything or not. You can give the user a hand in locating the new or updated information by directing the user’s focus to the help text via an anchor tag, or the text could simply be displayed at the user’s current point of focus. Obviously these solutions aren’t perfect, but they are certainly better than nothing.

Ways to alert the user is an area in need of much more research, and we hope to bring you articles in the future which address this problem in greater depth.

JavaScript Events

The following events were tested with Jaws for Windows 9.0 and Window Eyes 7.0 Beta 1, using Internet Explorer 7. Firefox and other browsers were not tested, as screen reader support for those browsers is quite poor, especially when it comes to JavaScript. As a result, use of “alternative” browsers is much less common among blind users.

click

Jaws and Window Eyes both alert the user when an item on the page is clickable, with one major exception: as all good JavaScript developers know, there are two models for attaching events to HTML elements. There is the traditional model (<span onclick="someFunction()"> or element.onclick = someFunction;) and the advanced model (element.addEventListener(...); or element.attachEvent(...); depending on which browser you are coding for).

Of course, it shouldn’t matter which model is used, right? Wrong. For some reason, both Jaws and Window Eyes will not tell the user that an item is clickable if the click event was registered via the advanced registration model.

In Jaws, if the user happens to press Enter on a part of the page which is not reported as being clickable, a click on that portion of the page is passed to the browser anyway, so if there is a click event attached to that portion of the page, then it will fire. Of course, this is rather improbable, as the user isn’t likely to try to “click” places which aren’t reported as being clickable. Also, in Window Eyes, any press of the Enter key on a portion of the page with no link, form control, or element which is not reported as being clickable is ignored.

Fortunately, there is a simple hack which will force Jaws and Window Eyes to recognize that a given element is clickable when you use the advanced registration model. Simply register an empty click event using the traditional model (onclick="" or element.onclick = "").

It appears that the user can fire traditionally registered click events by pressing Enter on any non-form element in both screen readers. However, in Jaws, only click events attached to checkboxes and radio buttons can fire, and in Window Eyes, it does not seem to be possible to easily fire click events on any form controls.

dblclick

Neither screen reader reports when the dblclick event is attached to any element, and this event is not easily fired in Window Eyes. In Jaws, it is possible, but again, rather improbable. If a user presses Enter twice quickly on a non-form element with this event attached, it will fire. There does not seem to be a way to get it to fire with Jaws when attached to a form control.

mousedown, mouseup

Neither screen reader reports when the mousedown and mouseup events are attached to any element, and these events are not easily fired in Window Eyes. In Jaws, they will fire if the user presses Enter on a non-form element where either of them are attached. There does not seem to be a way to get either to fire with Jaws when attached to a form control.

mouseover

Window Eyes does not support the mouseover event, and there is no way to make it fire with that screen reader. On the other hand, Jaws seems to offer surprisingly good support for this event, though again that support breaks down somewhat when the event is registered via the advanced registration model.

Jaws will alert the user when a mouseover event is attached to an element via the traditional model, and it can be fired by use of the Jaws Mouseover hotkey (Insert+Ctrl+Enter). Here again, if the event is registered via the advanced model, Jaws will not alert the user that they can mouseover that particular element. However, if the user guesses that they can, the hotkey will still work. Pressing Enter on an element with mouseover attached will also cause the event to fire, but will additionally tell the browser that the user clicked there, which may cause other events to fire if attached.

In addition, the same hack that works on the click event also works with the mouseover event. If you attach an event using the advanced model, you can get Jaws to recognize that the mouseover event is attached by adding an empty event to the element via the traditional model.

focus, blur

In Jaws, there are two primary ways to navigate a web page. The first is by reading everything from top to bottom, and the second is by tabbing through all the focusable elements (links and form controls). Confusingly, this results in the user having two cursors, a reading cursor and a mouse cursor. The reading cursor exists only for the screenreader (think of it as the virtual buffer cursor), and it has no effect on the browser. The mouse cursor, of course, is just the mouse pointer. Jaws doesn’t generally move the mouse cursor unless it needs to. If the user reads through a page, and stops on a link or form control, the reading cursor is on the element but the mouse cursor is probably not. When the user presses Enter on that link or control, the mouse is then moved to the same position as the reading cursor, so it can be clicked. On the other hand, if the user tabs through the page, the mouse cursor is moved with the reading cursor, and each element gets focus by both cursors.

The end result is that if a user tabs from form control to form control, the current control gets focus and the preceding control is blurred. However, if the user just reads each control with the reading cursor, then no controls get focus and no controls are blurred.

Like Jaws, Window Eyes also has two cursors, but pressing the tab key or reading through the page will focus the mouse on each link as it is reached, and blur it as it is passed. Form controls, on the other hand, will only receive focus if they are modified or given focus when in forms mode.

With these limitations in mind, the blur and focus work as expected: before a form control is changed and before a link is clicked, it receives focus, so the focus event is fired on the current control, and if applicable, the blur event is fired on the previous control.

change

Works as expected in both Jaws and Window Eyes.

keydown, keyup, keypress

When in forms mode, works as expected in both Jaws and Window Eyes. When not in forms mode, the events can’t be made to fire, because Jaws and Window Eyes capture most keypresses.

select

When in forms mode, works as expected in both Jaws and Window Eyes. When not in forms mode, text can be selected and copied to the clipboard without firing the event. This is likely due to the fact that text is being selected and copied from the virtual buffer, and not directly from the browser.

scroll

In Jaws, this event is fired at seemingly random times, including sometimes on page load.

In Window Eyes, on the other hand, it fires at much more logical times—after the user has read several lines of text. However, keep in mind that all scrolling, as such, is taken care of by the screen reader. From the user’s perspective, there is no such thing as below the fold, as everything on the page is all in one big buffer, and screen readers give no indication to the user when they scroll the screen.

load

Works as expected in both Jaws and Window Eyes.

Compatibility Table

For a nice all in one spot reference, here is a compatibility table after the manner of the great Quirksmode Compatibility Tables by PPK.

event Jaws 9.0 Window Eyes 7.0 b1
click incomplete incomplete
dblclick minimal no
mousedown minimal no
mouseup minimal no
mouseover incomplete no
focus incomplete * yes
blur incomplete * yes
change yes yes
keydown yes yes
keyup yes yes
keypress yes yes
select incomplete * incomplete *
scroll minimal yes
load yes yes

* = Only works in forms mode

Conclusion

There is no widely adopted solution for helping screen readers interact better with JavaScript, though the W3C is working on one. WAI-ARIA promises to be the next big advancement in web accessibility, and, if implemented, could very well solve many of the issues identified above. Unfortunately, complete and wide-spread adoption of the standard is at least a couple years off.

In the meantime, what users need is more research by the accessibility community into ways to overcome the existing shortcomings of screen readers when interacting with JavaScript, and more work by the screen reader manufacturers to fix some of the issues that have been identified. We must begin to address the concerns of people who are trying to make their dynamic web applications accessible, or we will have no room to complain when the next batch of web applications prove to be unusable by the disabled.


This article was written by both Aaron Cannon and Aaron Barker, but our blog software isn’t that robust, so we had to associate it with just one person.

Thanks to PPK for his permission to let us use the compatibility table design, to Kurt Hills for playing the part of editor and to all the others who gave it a proof-reading.

posted by Aaron Cannon on Tuesday, Oct 07, 2008
tagged with accessibility, ajax, javascript, dynamic content, jaws, window eyes, screen reader, screenreader