Summary
Popular screen readers use a virtual buffer to allow users to interact with web content. This article uncovers undocumented behaviour in JAWS 7.1 and later, which allows web developers to build Ajax applications that update the virtual buffer without any interaction from the user.
Author: Gez Lemon and Steve Faulkner
Contents
The Virtual Buffer
In a previous article, we demonstrated how Ajax applications could be made accessible to screen readers. In essence, this involves screen reader users understanding the concept of a virtual buffer, and knowing how to update the virtual buffer. Understanding the virtual buffer is essential for empowering screen reader users, particularly considering the number of Web 2.0 applications that depend on Ajax. Screen readers typically take a snapshot of a web page, and place the content in a virtual buffer to allow the user to interact with the content.
Screen readers that use a virtual buffer offer commands to manipulate the buffer, such as P to navigate to the next paragraph. When a screen reader user attempts to enter data in a form, the virtual buffer cannot be active, or their keystrokes will be used to interact with the buffer, rather than interact directly with the form controls.
As an example, if a screen reader user were to enter the word Harry in a text edit box, then when they typed H for the first letter, most screen readers would announce the next heading (or previous heading if the user pressed Shift + H), as that is the purpose of the virtual buffer; it provides a convenient mechanism for screen reader users to interact with HTML content.
Toggling the virtual buffer off and on will also update the virtual buffer, although there is usually a specific command to update the virtual buffer. For example, the virtual buffer can be updated in JAWS at any time using Insert + Esc. This is the most empowering command for screen reader users — if a screen reader user interacts with an application that doesn't appear to be responsive, the best course of action is to update the virtual buffer and re-investigate the content.
Improvements in JAWS
In JAWS 7.1 and later, Freedom Scientific introduced behaviour that makes it a little easier for developers to consider screen reader users when developing Ajax applications. In previous version of JAWS, the virtual buffer was always updated one step behind when the spacebar
is pressed to activate a form control; that is, the virtual buffer contained the content displayed on the screen before the current update (this article only considers Internet Explorer 6 and 7, as although Firefox has limited support with JAWS, that support is way behind Internet Explorer, as this is what Freedom Scientific have chosen to support). In other words, there was a disconnect between what was displayed on the screen, and what JAWS reported to its users.
In JAWS 7.1 and later, dynamic updates that result from the user activating a link with the keyboard will automatically update the virtual buffer so that it is synchronized with the content on the screen. This is a significant improvement in JAWS, and means that users don't have to be so concerned about updating the virtual buffer themselves. However, a lot of Ajax-type applications typically update content in response to a variety of form controls, and JAWS does not automatically update the buffer in response to these controls. The virtual buffer is also not updated when a mouse is used to interact with an interface element. This is probably because Freedom Scientific consider that the majority of their users are blind (and unable to use a pointing device), although it is common for people with learning/cognitive difficulties to also use screen readers.
The Update Virtual Buffer Function
In JAWS 7.1 and later, certain script statements cause the virtual buffer to be updated without any interaction by the user. For example, under certain conditions, content updated using innerHTML
will cause the virtual buffer to update; after the update, the virtual buffer won't contain the content added with innerHTML
, but will contain the content before the function was called, so this could be used at the end of an Ajax call to update the virtual buffer. On the surface, this appears to be great news, but innerHTML
is not DOM compliant. On further investigation, we discovered that the virtual buffer is also updated when the setAttribute
method is called to update a form control's value
. With this in mind, we realised that we could automatically update the virtual buffer in JAWS 7.1 and later with two simple functions; one to add a hidden form element to the document, and another to update the hidden element in response to an Ajax call. The function prepareBuffer
adds a hidden input
element to the end of the document with the name, virtualbufferupdate
. This function should be added as soon as possible (ideally, when the page loads).
function prepareBuffer()
{
var objNew = document.createElement('p');
var objHidden = document.createElement('input');
objHidden.setAttribute('type', 'hidden');
objHidden.setAttribute('value', '1');
objHidden.setAttribute('id', 'virtualbufferupdate');
objHidden.setAttribute('name', 'virtualbufferupdate');
objNew.appendChild(objHidden);
document.body.appendChild(objNew);
}
The function updateBuffer
updates the value of the hidden form control, which in turn updates the virtual buffer. This function should be called towards the end of the callback function that is assigned to the onreadystatechange
event handler for the Ajax call.
function updateBuffer()
{
var objHidden = document.getElementById('virtualbufferupdate');
if (objHidden)
{
if (objHidden.getAttribute('value') == '1')
objHidden.setAttribute('value', '0');
else
objHidden.setAttribute('value', '1');
}
}
The pseudo code for our complete application may look as follows:
function init()
{
// Statements required by the init function
someElement.onclick = function(){return updateContent(this);};
prepareBuffer();
}
function updateContent()
{
// Setup XML HTTP request object
// ...
if (objXMLRequest)
{
objXMLRequest.onreadystatechange = processResult;
objXMLRequest.open('GET', 'somefunc.php', true);
objXMLRequest.send(null);
}
return true;
}
function processResult()
{
// When we're sure the status of the XML HTTP request
// object is suitable, do our thing ...
updateBuffer();
// focus on an object if need be
}
Conclusion
JAWS 7.1 makes significant improvements over previous versions, in that the virtual buffer is automatically updated in response to a link being activated by the keyboard. The updateBuffer
function presented here extends the limited improvements in JAWS 7.1 and later, by providing a mechanism to update the virtual buffer for other interface elements, that works regardless of input device. This means that users of JAWS 7.1 and later do not need to explicitly update the virtual buffer in order to interact with Ajax applications. Of course, this depends on the developer providing an updateBuffer
function, or equivalent, so it is ultimately always far better that screen reader users understand how the virtual buffer works. This hack does, however, at least provide a method for developers to provide confidence to screen reader users with JAWS 7.1 and later in a way that does not have a detrimental affect for other users.
Translations
- Italian, kindly provided by Roberto Castaldo
Category: Accessibility.
[improving-ajax-applications-for-jaws-users.php#comment1]
This is great. Thank you Gez and Steve for your hard work.
Posted by Ted Drake on
[improving-ajax-applications-for-jaws-users.php#comment2]
This is great information, as always. Thank you for being so committed to accessibility issues.
Since many (most?) screen reader users will have a pre-7.1 version of JAWS for months, if not years, to come, don't forget, developers, to write your code so that it degrades gracefully. Bells and whistles aside, this will help ensure that your apps are accessible for most everyone.
Thanks!
Posted by Mike Elledge on
[improving-ajax-applications-for-jaws-users.php#comment3]
I too will throw in my congrats on extremely useful information here guys. Well done... I am now about to do an email blast to the entire campus.
Keep up the good work!
Posted by John Foliot on
[improving-ajax-applications-for-jaws-users.php#comment4]
I really don't know what "degrade gracefully" means in terms of Ajax. In fact, I think it may have no meaning at all. If we could do things without JavaScript, we would. If you have JS turned off (not that anyone does) or you have something functionally equivalent in a screen reader that can't follow updated sections of a page, well, you're just going to miss out.
There is no way to progressively enhance every Ajax application so that JS and no-JS are equivalent. That's outdated WCAG thinking that has been overtaken by events. Perhaps we should stop reiterating "degrade gracefully" as though it were some kind of koan.
The solution is to make JavaScript and Ajax applications and adaptive technology accessible and mutually compatible. The techniques here are a hack until that happens.
Posted by Joe Clark on
[improving-ajax-applications-for-jaws-users.php#comment5]
Joe Clark:
Indeed it is "a hack", which we acknowledge:
Posted by Steve Faulkner on
[improving-ajax-applications-for-jaws-users.php#comment6]
Really good work Gez and Steve.
I somewhat agree with Joe but not entirely. Until we have some way to define what should be highlighted to the user on change and what shouldn't screen readers which automatically follwed updating content, or announced updates as they happen could be really detrimental.
The web is becoming applications the screen reader can't anticipate the needs of the application and if they try I foresee rather a lot of false positives.
This technique is good in that it's still up the web application developer to focus on the right place after updates, or not at all.
I wrote some more thoughts about this post, that are not related to Joe's comments on my blog.
http://www.kid666.com/blog/2007/01/19/escape-from-the-jaws-of-inaccessibility/
Posted by Tom on
[improving-ajax-applications-for-jaws-users.php#comment7]
Muchas gracias, Gez and Steve. Sterling work, once again.
When it comes to Ajax and screenreaders, all we have to work with are hacks: we're hacking around the limitations of the technologies. As hacks go, this is pretty damn clean.
Posted by Jeremy Keith on
[improving-ajax-applications-for-jaws-users.php#comment8]
Good work guys.
It looks like a practical workaround that can facilitate behind the scenes buffer updates for legacy UA's without burdening the user with info about the buffer at all.
Though if screen reader users knew more about the buffer that would be no bad thing
Jeremy Keith said:
Thats exactly right, as there is a disconnect between where we are at and the technologies promise.
Posted by Joshue O Connor on
[improving-ajax-applications-for-jaws-users.php#comment9]
Gez and Steve,
Very interesting! The question is open as to whether it's a good idea to actually use the technique. I've used lots of hacks in my time and don't know if I want to use more.
In any case, I'm also curious about affects, adverse or otherwise, on other technologies. What if GW Micro adopts the technique for Window Eyes next week?
To that end, I've built a couple of test cases over at Access-Matters. Amble on over and try then out:
http://www.access-matters.com/2007/01/22/improving-accessibility-for-todays-ajax-to-hack-or-not/
Posted by Bob Easton on
[improving-ajax-applications-for-jaws-users.php#comment10]
That's fair enough; we're not trying to force techniques on people. We're merely suggesting techniques that can be used by people who want to make Ajax more accessible.
If Window-Eyes update their virtual buffer in response to this technique (they don't at the moment), that would be a good thing, as there wouldn't be a disconnect between what is displayed on the screen, and what is reported to the screen reader user. Why do you see that as a bad thing? More importantly, if JAWS, Window-Eyes, and every other assistive technology eventually work well with rich internet applications, developers that employ this technique can simply remove it.
Posted by Gez on
[improving-ajax-applications-for-jaws-users.php#comment11]
Bob, the 2 test cases you have created are going to provide the same result for JAWS 7.1+.
To clarify: In JAWS 7.1+ the updatebuffer function is not needed on a link that triggers an AJAX style update, if it is activated via the keyboard. The virtual buffer update is provided by JAWS. What our hack does is to extend the (half-assed) functionality implemented in JAWS by Freedom Scientific, to the button element and input type [button, image, checkbox, radio button]. It also extends the functionality to all of the above including links when they are activated via a mouse click.
I would be very surprised if this technique had any adverse effect upon other AT as all that it is doing is changing an input type=hidden value attribute's value. I have checked it with the latest version of Window Eyes (6.0) and found that unfortunately it stubbornly refuses to update the virtual buffer.
Posted by Steve Faulkner on
[improving-ajax-applications-for-jaws-users.php#comment12]
Gez said:
Sorry, that wasn't my intent. I see it as a GOOD thing when any of the AT becomes more responsive. OTOH, that responsiveness really should be triggered at a lower level, ideally at the operating system level so the AT doesn't have to depend on endless layers of detection.
Posted by Bob Easton on
[improving-ajax-applications-for-jaws-users.php#comment13]
Steve said:
Thanks for that clarification. I jumped too quickly to the simplest case, a link. When I can get to it, I'll update the test cases to use a button.
Nor was I expecting any adverse effects. Just seeking to document what helps. And no, I don't really expect earlier versions of JAWS to be helpful. Yet, here will be test cases for people to try for themselves.
Thanks again!
Posted by Bob Easton on
[improving-ajax-applications-for-jaws-users.php#comment14]
Thanks Steve for clarifying the purpose of the hack, and Bob for promising to update the test cases as this jaws user was for one a little confused by the similarity in result and expected behaviour in both cases. To continue the concern I raised over at Access-Matters, while removing the need for the user to manually update the buffer is certainly a very significant improvement the fact remains that causing an action on one part of the page to provoke a response elsewhere on the page is problematic for users of a screenreader which can by any nature only focus on any one point at a time. Might one usercentric use of this hack be to inform the user at the point of the activated link (button etc), (possibly via the form label / description) where on the page content has changed, or will change.
Posted by Adrian Higginbotham on
[improving-ajax-applications-for-jaws-users.php#comment15]
I have been trying to live in the AJAX Web 2.0 world as well as in the accessible world. I have started using a dual approach where I use AJAX to add dynamic content to pages when not in the accessible mode; and I use an embedded iframe to display the dynamic content when rendering an accessible page. I carefully set focus to the iframe when the dynamic data finishes being loaded. My question is whether this iframe approach to showing dynamic data on the page is workable with the various readers.
Thanks,
Peter
Posted by Peter Hurley on
[improving-ajax-applications-for-jaws-users.php#comment16]
This is a good point, and something I'm working on at the moment. This particular problem will be solved when WAI-ARIA is a stable recommendation and supported by user agents, but is a significant problem today. I'm currently working on a technique that bridges that gap, but uses WAI-ARIA recommendations when supported.
Yes, updating a screen reader's virtual buffer is only a small part of the problem; without an announcement, screen reader users might not realise that anything has happened. Even when they are aware something has happened, there is still the issue of allowing screen reader users to investigate the document without forcing them through the updates. The announcements also have to be carefully handled, or screen reader can end up overloaded with a queue of update announcements. I'm hoping to have some follow-up information on this soon.
Posted by Gez on
[improving-ajax-applications-for-jaws-users.php#comment17]
Hi Gez, great article
Joe said:
About JS not being switch on, there was a valid point from media 2006 which Jeremy Keith talked about. This was about "not creating important content using the DOM". So I guess using JS would not be a problem if you site can still deliver the content intended if JS is switch off?
Posted by Gaz on
[improving-ajax-applications-for-jaws-users.php#comment18]
The issue of screen readers and how they handle updates to page content came up during Bruce Lawsons talk at WebDD and then again in Dave Verwers presentation later on, obviously something which there's a lot of confusion over so info like this is always welcome.
Cheers Gez, good to see you're on the case
Posted by John Baxendale on
[improving-ajax-applications-for-jaws-users.php#comment19]
It would really be extremely useful to have a test case for this
problem. As it currently is, I do not understand the exact nature of
the problem. At least with jfw 8, the virtual buffer seems to get
updated fine as a result of form actions. For example:
http://openprinting.org/printer_list.cgi
Consider the first two comboboxes on the page. If a printer
manufacturer is chosen from the first combobox, the second one is
updated to list the printers from the selected manufacturer. This
update is clearly done via javascript, more specifically via the
onchange event handler on the first combobox. I guess this is thus not the
kind of situation the article is aiming at, but what is it then?
Posted by Lukas on
[improving-ajax-applications-for-jaws-users.php#comment20]
ATs like screenreaders are falling through the net of progressive enhancement - it's not enough to provide a JS application that degrades gracefully to non-JS, because the reader *is* a JS device, but it needs the non-JS version.
So until such time as: ATs can deal with Ajax apps just like graphical browsers; or, we know that all AT users have JS switched off; Ajax is and remains an immature technology that is not yet ready for primetime use.
Ajax is not cool, but it will be cool! But before we can begin to developer proper, accessible Ajax, the first thing we must do is back down.
This is a great article and a step closer to the holy grail. Sure it's hack, but *all* web applications are hacks - HTTP was not designed to have state
Posted by brothercake on
[improving-ajax-applications-for-jaws-users.php#comment21]
Lukas said:
There are many situations where JAWS 7.1 and later updates the virtual buffer correctly, but there are also cases where the virtual buffer is not updated. For example, the latency from an Ajax call might result in the virtual buffer being updated before the
onreadystatechange
event has completed. Another example is with custom controls, such as a tree view, where JAWS doesn't realise that the virtual buffer should be updated in response to user actions; this particular example also has other issues, but this article is just interested in the virtual buffer representing what is actually on the screen. Updating content in a hidden form element forces JAWS to update the virtual buffer, so if it's called after the DOM has changed, the virtual buffer matches what is actually on the screen.Posted by Gez on
[improving-ajax-applications-for-jaws-users.php#comment22]
Brothercake said:
I completely agree that Ajax is an immature technology; or at least, as a technology, it lacks a convenient mechanism of notifying assistive technology that the view has changed, what parts of the view have changed, and providing a non-invasive method of allowing users to easily investigate those changes. WAI-ARIA's AJAX Live Regions will bridge that gap, but that route also depends on user-agent support. Considering that some assistive technologies, such as screen readers, are incredibly expensive, it's not reasonable to assume that the problem will be solved at the point WAI-ARIA becomes a recommendation with support from the latest versions of screen readers.
Unfortunately, the lack of a decent accessibility architecture and user-agent support won't stop or slow down the production of Ajax-based applications due to their perceived coolness. Like you, I would prefer people didn't produce these types of applications until the support is properly in place. At the same time, if developers are going to continue to produce Ajax-based applications, I would hate to see other areas of accessibility being overlooked. If we can't persuade people to wait until the technologies are in place, I think the best we can do at this point in time is to highlight the problems these kinds of applications cause, suggest workarounds where possible, and emphasise that other areas of accessibility (people with mobility disabilities, cognitive disabilities, sensory disabilities, such as poor vision, hearing impairments, etc.), are equally as important as people who are blind and depend on screen readers.
Cool - thank you.
Posted by Gez on