Fixing anchor focus in Safari and Opera
Posted 2 November 2009 by Russ in jQuery. Tags: accessibility, jQuery, Opera, Safari.
The latest versions of Safari and Opera (4 and 10 respectively at the time of writing) both have a strange quirk: when you click on a link to an anchored element (ie a link with #some-id), the browser scrolls the viewport to this relevant anchor but does not shift the focus.
This isn’t immediately obvious or a problem until you throw your mouse to one side and navigate through your site with your keyboard, as some users will choose/have to do. The expected behaviour would be for you to click on a link and then for the focus (and therefore your cursor and screen reader) to shift to that anchor so that you can jump around the page as required.
A prime example is the accessibility navigation you often find at the top of a page that contains links to skip to primary navigation, skip to content, etc.).
Well, over at Communis they’ve come up with a small piece of script to tackle this problem and it works great for a single ’skip content’ link. So below is a function you can use to fix any #anchor url on a page, the only prerequisite being the inclusion of the jQuery library (just because it’s always there as the library of choice on my projects), and a hat tip to Communis for kicking this off…
The markup
Pretty simple this – just any old link pointing to an anchor on the current page.
<ul> <li><a href="#test-1">Jump to and focus on #test-1</a></li> <li><a href="#test-2">Jump to and focus on #test-2</a></li> </ul>
The script
Then we have our script. I tend to create all my scripts inside their own object namespace to prevent any naming conflicts with other scripts that may be present on a page so first we have an object called Template.
// object namespace
var Template = {};
Then we extend this by adding a method called fixBrowserFocus(). This function will quit if the detected browser is not an instance of Safari or Opera but if it does detect one of these browsers, we start by declaring a couple of regular expressions that we use for working with the URL strings in each link.
/**
* Forces Safari and Opera browsers to set focus on anchor elements
* Based on http://www.communis.co.uk/blog/2009-06-02-skip-links-chrome-safari-and-added-wai-aria
* Also see point 9 of http://www.webcredible.co.uk/user-friendly-resources/web-accessibility/errors.shtml
*/
Template.fixBrowserFocus = function() {
// quit if not safari or chrome browser
if ($.browser.safari === false && $.browser.opera === false) {
return;
}
// regular expressions for href and hash
var hashExpression = /[\w-_]*/,
locationExpression = /^[a-zA-Z0-9:\/%\.-_]*/;
Then we have a simple click handler method that is attached to any a or area element on the page that has an href property containing a hash to denote a link on the current page.
Each time you click on one of these links, the function checks if the anchor can be found on the page and if so, updates the tab index and sets the focus.
// click handler for links that have anchors in their href property
$("a[href*='#'], area[href*='#']").click(function () {
var parts = this.href.split("#"),
target = null;
// if the anchor is found on this page, update the tab index on the anchor and focus the browser on it
if (parts[0].match(locationExpression)[0] === location.href.match(locationExpression)[0]) {
target = document.getElementById(parts[1].match(hashExpression));
if (target !== null) {
target.setAttribute("tabIndex", "0");
target.focus();
}
}
});
}
Then all that remains is to call the Template.fixBrowserFocus() method when the DOM is ready, and we’re done.
$(document).ready(function () {
Template.fixBrowserFocus();
});
Example in action
Obviously, you’ll need to be in Safari or Opera to get anything when you view this test…. Feel free to download this example too.


Will
02. Nov, 2009
Good point, I’ll be off to try that out sharpish.
One point to raise though is that $.browser is deprecated in jQuery and may well be dropping out in future versions. I will also now be writing a home-brew version of that sometime soon.
Russ
02. Nov, 2009
Indeed it is and I’m fully in favour of jQuery’s object detection. This is the first time I’ve had to target Safari and Opera in this way though so I haven’t had a chance to come up with a way to target those browsers with object detection only.
kl
02. Nov, 2009
I’d use event bubbling instead of static handlers on all (currently present) anchors, so it wouldn’t interfere with other onclick handlers, would nearly have zero initialization cost, and would work for all future links.
Dan
09. Feb, 2010
Great fix thanks! A couple of small improvements if I may..
1. $(target).attr({ tabIndex : -1 }).focus(); // is a bit more compact
2. changing the tabIndex from 0 to -1 prevents div#anchor from becoming part of the future tab order in Safari