AJAX v Accessibility on Rails: Fallbacks
I’ve tentatively placed a toe aboard the AJAX bandwagon, thanks largely to Rails which makes it possible, even painless, to achieve AJAX functionality without having to learn Javascript.1
While AJAX, done properly, can be a big win for Usability for those whose browsers support it, I am also concerned with issues of Accessibility, and indeed keeping things usable for those with non-AJAX browsers.
Standards-Schmandards have come up with some useful hints about how to make AJAX pages more accessible to screenreaders, such as providing an option to pop up an alert box instead of, or as well as, simply updating the page. This is good advice, but it’s only half the story. These methods don’t help those without Javascript at all, or with non-AJAX-aware browsers.
In this article I will detail one method to help ensure accessibility for such people, without compromising on AJAX spiffiness, and most importantly without causing much extra work for us poor developers. The example is in Rails, but may be of interest to those using other frameworks.
Alternative alternatives
The bad way to provide a non-Javascript alternative is to provide two completely separate versions of the site; detect whether Javascript is enabled on entry to the site, and decide which version to show. Personally I’m amazed that people still do this in the 21st century, but they do. And what usually happens is that the non-Javascript “accessible” version often only has a fraction of the content of the main site in the first place, and then gets forgotten about, so is soon out of date compared to the main site.
The better way is to write just one version of the site, which works fine in all types of browser. Some developers seem to think this is near impossible. It certainly used to be quite difficult (unless you restricted yourself to fairly simple markup), but it’s getting easier all the time thanks to W3C standards, and frameworks like Rails. There is no excuse anymore.
Achieving graceful degradation is simply a matter of providing a non-AJAX fallback method for any important action that you choose to implement in AJAX. Sites that care about accessibility (and that ought to be most of them) should not rely on AJAX without providing an alternative for those who can’t use it.
Providing fallback in Rails
Rails makes it about as easy as it could be to provide non-AJAX fallback, but it still requires a bit of thought on the part of the programmer.
Let’s take an example of an AJAX-enabled shopping basket application. For AJAX browsers, we want a click on an item’s “Add to basket” button to update the BasketStatus div immediately. For everyone else, a click on the same button should send a normal form post with the relevant details, and return a full page render — which could be a re-render of the current page with the updated div, but is usually a new page showing the basket’s contents with links to “Checkout” and “Continue Shopping”.
form_remote_tag
is what you’ll be using for any action which changes something, like the contents of your shopping basket. This does provide an automatic fallback form submission for non-JS browsers, which by default will submit to the same controller action as the JS version. But you need to do a bit more work — if the submission was done by AJAX, the action needs to render a small snippet of Javascript or HTML to update a small part of the screen. If the form has been submitted in a non-JS way, you need to render a whole page!
How to detect whether a request was AJAX or not
The way I handled this at first was to have two separate actions in the controller, one for AJAX, one for non-AJAX. The common code that they both had to execute was hived off to a private method that they both called, and then they rendered differently as appropriate.
However, there’s an easier way. A single controller action can determine whether it was called by AJAX as follows:
def some_action ## do common stuff if request.xml_http_request? ## This was an AJAX request, so render a partial render :partial => ... else ## This was a regular form submission, so render full page render :action => ... ## OR redirect to some other page redirect_to :action => ... end end
It goes without saying, but I’ll say it anyway, that it’s a good idea to test that your buttons behave in both AJAX and non-AJAX mode. For this I wouldn’t be without Chris Pederick’s Web Developer Extension for Firefox, with which you can easily switch JS on and off (and a million other things).
Using link_to_remote
To get link_to_remote
to provide a fallback for non-Javascript is a bit more involved, as it doesn’t provide one by default (href="#"
). However, don’t be tempted to use links where you should be using forms. This function should only be used in two contexts:
- Links which are only expected to function under AJAX — ie it isn’t critical that they don’t work, or are missing, in other browsers (missing would be better — I’ll leave that as an exercise.)
- Links which call up static text or otherwise idempotent content.
For the latter case, you’ll need to make use of the third parameter to link_to_remote
, namely html_options
, to explicitly set the href
to point to your fallback URL, along these lines:
- Instead of:
link_to_remote "Do it", :action => 'my_action', :update => 'mydiv'
- You’ll need something like:
link_to_remote "Do it", { :action => 'my_action', :update => 'mydiv' }, { :href => url_for(:action => 'my_action') }
Don’t use link_to_remote
to make state changes like adding to a basket. Apart from the difficulty of providing fallback, if you do this you will effectively have URLs on your site which can directly enact session state changes without POST data. If you don’t understand why this is a bad thing, try it and see what happens when the search engine robots arrive.
1 Catalyst apparently integrates AJAX to some extent too, but I was already finding Catalyst a bit painful before I got as far as the AJAX support. Despite the fact that I’ve been a Perl programmer for 10 years and a Ruby programmer for 3 days2, I find Rails rather more pleasant to use.
2 While the basic usage of Rails and Ruby is well documented, there’s currently quite a paucity of discussed examples. Hence my contribution here, which might seem precocious for one of such short experience, were it not that so few people are bothering to write at all. If I’ve made any errors, please let me know and I’ll gladly correct them.
March 5th, 2006 at 11:16 am
very good article – thanks a lot – i was searching exactly this information
July 25th, 2007 at 4:50 pm
Very nice.
November 5th, 2008 at 9:25 pm
Thanks from 2008 🙂