Quick accessibility fix with jQuery
- Posted
17 November 2006
IE 7 has inherited the IE 6 problem with keyboard navigation and inline anchors. The otherwise time consuming fix was made simple with little a JavaScript – made even easier with jQuery. JavaScript to improve accessibility! Who would have thought…
The accessibility problem
By using a keyboard I mean tapping the tab
key to move from link
to link through a web page. This method works well to get until you want to
skip some of the content by following a link to an anchor that is within the
content of the page. The page will scroll down appropriately to the location
of the anchor, but tapping tab
once more to keep moving
doesn’t always do what you may expect. Instead of moving the focus to
the next link after the anchor it will move to a different link
somewhere near the top of the page – often scrolling the page back up without
regard for where you thought you were at in the page. This is
disorienting to me – imagine what it is like for someone reliant on a screen
reader to tell them what is happening on the screen!
Two kinds of anchor
The true nature of this problem seems to be obscured by the complexity of it’s triggers. Rather than attempt to figure it out definitively I’ll just stick to the problem I was experiencing. There are two kinds of destinations for same page anchors in my project:
-
div elements which appear on every page to divide the page into meaningful chunks such as (navigation vs. content):
<div id='templatesection'>...
(these work fine); -
And inline anchors which our client enters in throughout long pages of content to make it quicker to navigate
<a id='contentsection'></a>
(uh oh, trouble).
The workaround
To cut a longer story short you can fix it by wrapping a span around each inline anchor;
<span class='anchor'><a id='contentsection'></a></span>
and add a line to your CSS:
.anchor {display:absolute;}
Markup baggage
Unfortunately the site I am working on has many hundreds of these anchors peppered through it’s content. Each one has been individually inserted by our client with the link tool sported by WYSIWYG toolbar of the site’s CMS. Even if we hacked the code of the toolbar to change the mark-up it produced when inserting anchors. It would be necessary to reinsert every one.
Using jQuery to add the markup
I was considering pestering one of our developers to do a bit of database search and replace monkeying when I realised how easy it would be to add the extra code with some jQuery driven JavaScript.
If you are like me – HTML and CSS savvy, but not quite up with the pack in the JavaScript department – jQuery is the shit when it comes to DOM scripting JavaScript libraries. I hope this little example helps communicate my enthusiasm for it.
The code
$(document).ready(function(){
$("a[@id]").not("[@href]").wrap("<span class='anchor'></span>");
});
That’s it! With the (pleasantly trim) jQuery library loaded, this
script will find all the anchors in the page that have an id
but
no href
and enclose each of them in a span with a class.
Links in the chain
To the uninitiated the code above will look arcane. Broken down it’s simple.
-
This code ensures that any functions it contains are not fired until the DOM is ready – generally well before all the content (especially images) of the page is loaded.
$(document).ready(function(){ });
-
Firstly I need to specify which elements in the document I wish to modify. Let’s start with the most basic expression before narrowing it down. This tells jQuery (represented by the ‘$’) to find every
<a>
in the document. Just as in CSS wherea{};
refers to every<a>
.$(document).ready(function(){ $("a"); });
-
With a ‘selector’ defined I can simply add to the chain to manipulate each of those elements. This will add a span around every
<a>
:$(document).ready(function(){ $("a").wrap("<span class='anchor'></span>"); });
-
Of course I don’t want to do that! This would do crazy things to the regular links in my page. To discriminate between the links and anchors I need to be more specific.
My anchors each have an
id
assigned which I’m pretty sure the regular navigation links don’t have. So I can use that to refine the selector expression for jQuery. Adding the square brackets immediately after the ‘a’ in the expression provides a context to check something about this element in particular. Inside which, the@
refers to an attribute of that element, in this case it’sid
attribute. All upa[@id]
selects every<a>
with anyid
:$(document).ready(function(){ $("a[@id]").wrap("<span class='anchor'></span>"); });
-
Now my list of
<a>
elements in the page only includes those that have anid
. I’m pretty sure I don’t have any regular links withid
s… To be absolutely sure it would be a good idea to remove all the elements from this list that have ahref
.This isn’t done by further refining the expression like I have done so far. Instead I am adding a
not()
to the chain – between the$()
and thewrap()
. This whittles down the list of elements found by the initial expression – removing any that match this further check before the list is passed along down the chain for ‘wrapping’. The ‘not’ expression should look familiar now – except this time we are checking forhref
instead ofid
. Also notice there is no need to include thea
in this expression. The list of elements is already limited to those that have passed the first test:$(document).ready(function(){ $("a[@id]").not("[@href]").wrap("<span class='anchor'></span>"); });
Perhaps my explanation has puzzled your curiosity sufficiently to look at jQuery for yourself. Check out the official site. It has excellent documentation and astounding plugins. Even AJAX looks easy with jQuery!
Medicine. but not a cure
This coding cleverness doesn’t resolve the keyboard navigation problem for those of us unlucky enough to need to use Internet Explorer without JavaScript. Nor does it solve the same problem for Safari users – with or without JavaScript! Misuse has earned JavaScript a bad reputation when it comes to web accessibility. I hope this does a little to demonstrate it can be a power for good too.
Further thoughts
21 November 2006
Seems this script breaks some anchors altogether in IE 6. So much for enhancing accessibility! I’ll be sure to post something here when I get to the bottom of it.