Learning to focus()

Heydon Pickering

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.

For accessibility, managing focus is essential in many of even the simplest JavaScript-driven interactive widgets, but correct usage is almost impossible to test for with automated QA tools. That’s why I want to take you through a couple of simple examples today.

ScrollTop Animation

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 (#section1, let’s say), you are ushered gently to this destination via a JavaScript scrollTop animation. The important thing to note regarding accessibility when using this technique is the necessity to override the default function of the <a> element.

event.preventDefault();

By doing this you are telling the browser to avoid, at all costs, doing anything standard, expected or interoperable. You are telling the link not to link. In fact, the only way to make certain the link actually takes the user correctly to the page fragment is to turn JavaScript off. Well, then.

By simply linking to a page fragment — as one would with JavaScript off — the browser essentially focuses this fragment. This is where screen reader output and keyboard interaction is now based. By animating the 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.

The Remedy

We need to focus the destination fragment via JavaScript. First we need to make the fragment programmatically focusable to begin with, which requires use of the 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>

Our second task is to include the JavaScript focus() method in the callback of the animation, ensuring the fragment is focused after the animation ends.

document.getElementById('section1').focus();

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>

The Result

A little CodePen demo is embedded below. Try using it with just TAB and SHIFT+TAB as a habitual keyboard user would.

See the Pen idxjL by SitePoint (@SitePoint) on CodePen.

Take note that we are adding the tabindex attribute on the fly in our JavaScript. Now that we are focusing the sections (fragments) to which we are scrolling, our perceived location is more than merely visual. That is, should I press the TAB key after moving to a new section, I will focus the next focusable element within that section; the hyperlink reading ‘heydonworks.com’, in the following example.

<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.

$('dialog button:first-of-type').focus();

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:

$(this).attr('data-dialog-trigger', true);

At the end of the dialog’s close() method, we would then just focus the original element:

$('[data-dialog-trigger]').focus();

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.

Conclusion

It is a common mistake to conflate progressive enhancement with accessibility; to think that making sure something degrades to working with JavaScript turned off (as it would in this case) means it is ‘accessible’. Sure, it makes it accessible to those without JavaScript, but most keyboard and screen reader users interact with your applications with the help of JavaScript like anyone else. The trick is to use JavaScript in a way that respects the differing behaviors and inputs of all your users and any assistive technologies they might use.

One more thing: If you are ever charged with working on a one page application built with, say, Ember.js or AngularJS, grep the codebase for the JavaScript 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.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • patrick h. lauke

    Why tabindex=”0″ and not tabindex=”-1″ ?

    • LouisLazaris

      @patrickhlauke:disqus:

      According to the WHATWG spec:

      The tabindex attribute, if specified, must have a value that is a valid integer. Positive numbers specify the relative position of the element’s focusable areas in the sequential focus navigation order, and negative numbers indicate that the control is to be unreachable by sequential focus navigation.

      • http://webaim.org/ Jared Smith

        I agree with Patrick that tabindex=”-1″ would be more appropriate in this example. This allows it to receive focus with JavaScript, but does not place it in the natural tab order of the page. Putting the section in the tab order with tabindex=0 suggests that it may be interactive which could be confusing to keyboard users, especially screen reader users that will not see that it is not an interactive element.

        • LouisLazaris

          I see what you’re saying. My initial reaction was that you don’t want it to be ‘unreachable’, but yeah, I guess it makes sense to take it out of the natural tab sequence. I’m not totally sure though. Let’s see what Heydon has to say when he chimes in. Thanks.

    • heydonworks

      You (and Jared below) are quite right, of course. By using tabindex=”0″ I include a permenant TAB stop, which may confuse some users.

      However, I find it astonishing that only buttons, links and form elements are focusable by default. Who decided that only interactive elements should be focused? A screen reader user can use the h quick key (thanks, Jared http://webaim.org/resources/shortcuts/jaws ) to jump between headings. Why is there no standard mechanism for kbd users to do this? If I press TAB in one section and it takes me deep into another section to focus a link (bypassing the section’s introduction) how do I know I’m in a new section?

      • http://webplatformdaily.org Šime Vidas

        I just noticed, in Opera/Windows, links are not reachable via TAB. What am I missing?

        • patrick h. lauke

          You’re missing the fact that, despite moaning about this for years even internally, Opera by default still only cycles through form controls with TAB/SHIFT+TAB. You need to explicitly go to about:config, Websites, and tick the “Pressing Tab on a webpage highlights links, as well as form fields” checkbox under Display. Only browser on Windows to force you to do this, all others have this by default …

          • http://webplatformdaily.org Šime Vidas

            Seems senseless.

          • heydonworks

            +1 for senseless. Jeepers.

      • patrick h. lauke

        I’d say the assumption is that keyboard users without a screenreader can see headings etc, and only need to jump to actionable elements. With a screenreader, reading something specific entails actually setting focus to it, whereas for a sighted keyboard user reading and setting focus to action something are two distinct actions.

        There are extensions and user scripts that can help keyboard users that want additional flexibility in how they can jump around the content, of course. But I can see the point why browsers don’t include this in their options, as it’s a bit of an edge case?

  • heydonworks

    No worries, that’s just a user agent style. You can improve the appearance of it or, in this specific case, remove it. Do not remove outline from standard focusable elements like links unless you put another :focus style in their place.