Learning to focus()
Thanks to good fortune, I have a functional pair of eyes. When it comes to traversing web pages, this is something of a boon since shifting my focus from one part of the page to another requires nothing more than semi-consciously contracting my extra-ocular muscles and rotating my eyeballs a fraction of their circumference.
Though my visual — and, therefore, cognitive — focus could now be said to have changed, the user interface itself does not know my attention has turned. At least, not until I begin to physically interact with the newly scrutinized region. To put it another way, rotating my eyeballs is an unrecorded event.
Herein lies a very common but frequently unaddressed accessibility issue: If I’m not using my eyes (and my mouse hand to point), how do I ‘focus’ on something in order to then interact with it? The answer, as I’m sure you know, is via programmatic focus. It’s not as simple as just incorporating ‘focusable’ elements into our designs, however. Sometimes it is not the user but the interface on the user’s behalf that must move focus between different areas. This requires deliberate focus management on the part of the developer.
In this example, I invite you to imagine you have ‘enhanced’ a same-page navigation link so that, instead of jumping abruptly to the link’s target fragment (
scrollTop, no such focusing action takes place, meaning screen reader output and keyboard interaction continues from the area of the page that is no longer in view. This is no good.
tabindex attribute. A
tabindex value of
-1 is a special value that means scripts can focus the element, but not users. This is preferable to
tabindex: 0 in this case, because there’s no reason why this non-interactive element should be focusable using the TAB key.
Note: Thanks to Patrick and others in the comments for pointing out the fact that this should use “-1” instead of “0” for the tabindex value, which we’ve now corrected.
<section id="section1" tabindex="-1"> ... </section>
focus() method in the callback of the animation, ensuring the fragment is focused after the animation ends.
Finally, it’s a good idea to record our sub-page location in the URL, as would have happened by simply linking to the fragment (e.g.
http://my-site/#section1). This way we are able to copy the address as a link to the specific section (i.e. we can “deep link”). Include the following line after
focus() has taken place:
window.location.hash = 'section1';
Of course, you would replace ‘section1’ with a variable based on whatever the corresponding link’s
href is, minus the
<a href="#whatever">scroll to section 'whatever'</a>
A little CodePen demo is embedded below. Try using it with just TAB and SHIFT+TAB as a habitual keyboard user would.
Take note that we are adding the
<section id="section1" tabindex="-1"> <p>Donec a congue leo? Fusce ac sodales magna. Aliquam nisl enim… tristique tempus placerat at, <a href="http://heydonworks.com">heydonworks.com</a> posuere in lectus. Curabitur consectetur faucibus nisl ac varius.</p> </section>
Had we not focused
section1, pressing the TAB key would have focused the next element following the instigating link, which would jump the viewport back to our navigation block. In other words, keyboard users would be back to square one.
<a href="#section1">Section 1</a> <a href="#section2">Section 2</a> <!-- not where we want to be -->
Closing Dialogs (Modal Windows)
Here’s another little example. Let’s pretend a user has pressed a
<button> and it’s opened a dialog or modal window. For argument’s sake, this dialog is asking the user to confirm or cancel the would-be action instigated by the button.
To make opening this button accessible, we should
focus() the dialog in much the same way as we focused the page fragment in the last example. Using jQueryUI, the first of the dialog’s buttons (‘confirm’, in our case) would be focused. Other implementations focus the dialog container. Either way, users are programmatically sent to the right place.
The question is, ‘what happens to focus when the dialog closes?’ If we do nothing, the newly hidden dialog would necessarily lose focus but nothing would take its place. In many user agents, this would mean the
<body> is focused by default, making users wade through the document to find their place again.
It would be more logical to refocus the element that opened the dialog. This is easy to do by just saving the DOM node in memory or to write a marker on it for later use, like this:
At the end of the dialog’s
close() method, we would then just focus the original element:
Most screen readers, upon the button being refocused, would announce the title of the page, then the focused button. That way, you know where you are. An example of a dialog using this method is available for testing should you wish.
focus() method. Such applications completely rewrite navigation using ‘views’; dynamic rebuilds of the singular page. Without some careful focus management, rebuilding the DOM in such a way is liable to screw up accessibility pretty quickly. If your grep finds less than a couple of
.focus instances, there may be a lot of work to do.