AJAX, JavaScript and accessibility

With the advent of mass-hype for building AJAX solutions, I find it necessary to shed some light of AJAX and JavaScript implementations and how they relate to and affect accessibility, and to explain how they can both co-exist; that one doesn’t exclude the other.

What is a progressive enhancement/unobtrusive JavaScript approach?

First, a good JavaScript approach is about implementing JavaScript in an unobtrusive way. Basically, what this means is avoiding some basic bad implementations:

  • No more inline event handlers in HTML elements, meaning that code like this should never be used:

    <div onclick="doSomethingAnnoying()">A div</div>)

  • Definitely never ever use javascript: links, like this:

    <a href="javascript:pleaseTeachMeProperJavaScript()">A destroyed link</a>

  • No inline JavaScript blocks in your web pages at all.

How should I do it then?

Common things to think about are:

  • Have all your JavaScript in external files, for better accessibility and performance (since JavaScript files are cached by the web browser and only needs to be retrieved once), and then also apply events to elements from there.
  • Only apply JavaScript event handlers to elements that already have built-in functionality for communicating, like links and submit buttons.
  • Make sure the web site functions without JavaScript. JavaScript is supposed to be used to spice things up based on already existing functionality, not to be the corner stone that the web site is totally depending on.

Give me a good example

Sure! For instance, say you want to apply a certain JavaScript event to some links in your web page that shows an information layer (e.g. a div that is initially hidden). How do you do it?

  1. Create your link elements:

    <a href="my-details.php" class="show-info-layer">My details</a>

  2. Include your external JavaScript file:

    <script type="text/javascript" src="linkEvents.js"></script>

  3. Use the window.onload event, which is triggered when the web page is fully loaded, to then apply your events to desired elements. There are many different ways of doing this and how to handle events, so here’s a simple example:
    
    window.onload = applyEvents;
    function applyEvents(){
    	if(document.getElementById && document.getElementsByTagName){
    		var arrAllLinks = document.getElementsByTagName("a");
    		var oLink;
    		for(var i=0; i<arrAllLinks.length; i++){
    	    	oLink = arrAllLinks[i];
    			if(oLink.className.search(/show-info-layer/) != -1){
    				oLink.onclick = function (oEvent){
    					var oEvent = (typeof oEvent != "undefined")? oEvent : event;
    					oEvent.returnValue = false;
    					if(oEvent.preventDefault){
    						oEvent.preventDefault();
    					}
    					document.getElementById("my-details").className = "display-block";
    				}
    			}
    	    }
    	}
    }
    

The result is that web browsers that have JavaScript activated and that support the document.getElementById and document.getElementsByTagName methods will cancel the links navigation to the my-details.php page and instead show an information layer directly in the page. For those who don’t match that criteria, it will simply redirect them to the my-details page. Offering something extra for those with JavaScript enabled but still degrading nicely and being fully functional to others.

Let’s break the script down, what happened?

window.onload = applyEvents;

First I tell the window to call a function when it’s onload event is triggered, i.e. the page is fully loaded. Notice: no parentheses after the function name, in that case it would’ve been called instantaneously.

In the applyEvents function, the first line is this:

if(document.getElementById && document.getElementsByTagName){

What it does is using an approach called object detection to see if the document object supports the two methods we want to use: document.getElementById and document.getElementsByTagName (these two are widely supported by most web browsers, don’t worry).

var arrAllLinks = document.getElementsByTagName("a");

Gets a collection of all link elements in the page (could be done in a more effective manner with the getElementsByClassName script).


for(var i=0; i<arrAllLinks.length; i++){
	oLink = arrAllLinks[i];
	if(oLink.className.search(/show-info-layer/) != -1){

Loops through the collection of links to find the ones with a certain class name. Note the usage of the variable oLink to avoid doing several checks in the array, and that it is also declared outside the loop. All for performance reasons.


oLink.onclick = function (oEvent){
	var oEvent = (typeof oEvent != "undefined")? oEvent : event;
	oEvent.returnValue = false;
	if(oEvent.preventDefault){
		oEvent.preventDefault();
	}
	document.getElementById("my-details").className = "display-block";
}

Applies the onclick event to the matching link/-s and cancels their default behavior. The check for oEvent in the event handling is the standard way of event handling, while event is for Internet Explorer’s flawed and proprietary event handling. Now a click will instead show the information layer element.

What about AJAX, it said so in the title?

With the good practices and examples I’ve given above, it’s pretty much all about using the same knowledge when doing something AJAX-based. With my AJAX library, ASK, it was my attention to implement it in that manner, and also cater to well-known usability problems like back buttons that work, impossible to bookmark a specific state of an AJAX-based page etc, at the same time. I definitely urge you to take a look at it and play around with it.

Something to think of is that when it comes to screen readers is that they might support the JavaScript you use but won’t notify the user that something has been updated in the page. For more on this discussion, please read Derek’s Javascript and Accessibility (yes, I saw the name of his post after I initially posted this one… :-) ).

 

Related reading

Posted in Developing,JavaScript |

Leave a Reply

Your email address will not be published. Required fields are marked *