Illustrated avatar of Sarah with a cat Sarah Higley

Focus management still matters

February 27, 2020

The most difficult challenges in programming are:

  1. Debating framework choices with a dogmatic fan, and
  2. Naming things

There is seemingly no solution for the first problem; you must accept it, adopt a zen-like calm when faced with invocations of bundle sizes and performance benchmarks, and silently plan your backup career doing literally anything else.

For the second problem, naming things, it's generally best to just pick something and move on. After all, any name is better than wasting hours agonizing over whether toggleMenuItemSelection is adequately understandable, descriptive, and concise.

And then, as a counterpoint, we have sequential-focus-navigation-starting-point.

You try saying "sequential focus navigation starting point" five times over the course of two minutes, while also trying to explain to someone exactly why they still need to handle focus after closing their dropdown, even though "tabbing works in Chrome." To prove my point, here is a brief list of names I would much rather use in said calls:

The party parrot meme, with overlaid text: starty focus party
Thank you for reviewing my application to name all future HTML features

Why are we talking about Something Focus Something Point?

The sequential focus navigation starting point is a browser feature that enables three types of interaction that were not possible without it:

  1. You can follow an internal link to a section or heading, and then start tabbing from that section/heading
  2. You can click anywhere on the page, and begin tabbing from that point
  3. You can hide an element with focus, e.g. by removing it from the DOM or applying display: none, without breaking tabbing.

All three behaviors enhance user experience, but the last one in particular has sometimes lured developers into a false sense of security about not handling focus when closing their popups, dropdown menus, or accordions. Although tabbing seems to work fine in these cases, the sequenced-focus-starts-somewhere-around-here-point is only a bandaid; manual focus handling is still necessary for robust accessibility.

Where did Sequential Focus Whatever come from?

The idea of a sequential focus navigation starting point began with the need to make same-page anchors work with keyboard navigation. Although same-page linking has been available for as long as <a> elements, keyboard support has often lagged behind visual mouse support, even at a platform level. So although sighted users would immediately have access to the linked section via scroll and could then interact with it using a mouse, keyboard users would have a different experience. As soon as an internal link was activated, focus would jump to the top of the document, and the next tab press would bring you back to the first focusable element in the page.

To fix this, browsers (possibly starting with IE5) began to send focus to the link target when following a same-page link. This only works if the target is focusable, though. Thus began the long period of recommending tabindex="-1" be added to to every heading, named anchor, or other static element targeted by an internal link.

The problem is, the visual behavior of internal links still works when the link target isn't focusable, and -- let's be honest -- that's all most developers test for. There was still an opportunity for browsers to improve the keyboard accessibility of this common built-in feature.

Firefox had a solution for this in place as early as 2013, which implemented starting-something-focus-whatsit-navigation-point in all but name. Although keyboard focus would still reset to <body> after activating a link with an unfocusable target, the next tab press would move focus as if the link target had been the active element.

Internet Explorer was the only other browser that handled non-focusable link targets, though it took a different approach and simply forced the target element to receive keyboard focus even if it was otherwise unfocusable.

Chrome took a bit longer (read: three years) to come around, but did finally implement the same focus behavior as Firefox in February 2016, with one addition:

If the element pointed by sequential focus navigation starting point is removed from the document tree, a point where there was the element at would be the starting point.

To summarize: when Chrome fixed tab order for internal links, they also used the same mechanism to save the location of the last focused item, if that item was removed from the page.

Around the same time, Rob Dodson wrote what is still the most thorough, reader-friendly explanation of sequential focus navigation starting point: Removing Headaches from Focus Management, in which he also mentioned that side benefit of gracefully handling focus when active element was hidden.

Incidentally, the phrase "sequential focus navigation why is this so long starting point" first appeared in the HTML specification in November 2014, where it was defined as an optional user agent behavior.

How SFNSP works today

The HTML spec for starting focus sequentially point still notes that it is optional, but if it exists it does the following (simplified slightly):

There is no mention in the spec of using focus-nav-start-point to handle keyboard navigation when the currently focused element is hidden or removed from the DOM. Despite the lack of official sanction, this behavior seems to be increasingly relied upon by web developers when closing open dialogs, dropdowns, accordions, or any other toggleable region. This conflicts with the prevailing wisdom that when removing a focused element from the DOM, an author must manually set focus on another (logical) node. However, if browsers save focus-navigation-starts-here-point where the removed element used to be, keyboard navigation seems to work fine even if focus is not handled.

As of writing, the following browsers match Chrome's behavior and set the sequential focus navigation starting point when the currently focused element is removed or hidden:

Of all the browsers on Windows and Mac that I have access to, only Internet Explorer and Edge (pre-Chromium) do not do this. You can try it out now by jumping to the next section and then hitting tab.

Overall, this is a win for accessibility. For someone relying on keyboard navigation (or any other assistive tech that navigates via focus, like switch devices), getting sent back to the top of the page every single time a developer forgot to handle focus is an enormous pain. The drawback comes when developers don't fully understand exactly what sequential-focus-belaboring-the-point does and doesn't do, or they don't notice focus bugs when keyboard testing.

Focus navigation starting point is not the same as actual keyboard focus. When focus is lost, through following an internal link or hiding a dialog, keyboard focus moves to the <body> tag. If you query document.activeElement in either of these cases, you can observe this. Alternatively, paste the following snippet into your browser console, jump to the next section and watch every focus update in real time:

document.addEventListener('focusin', () => {
console.log('Focus moved to:', document.activeElement);
});

That difference is extremely important when using assistive tech, since screen readers (for example) do not pay attention to the internal browser concept of focus-starts-here-point. While testing with a keyboard alone may make everything seem fine, trying the same interaction while running a screen reader exposes some rough edges.

Assistive Tech support

Tests were run against this (extremely simple and not otherwise very accessible) example that contains a few accordions and a dialog, only one of which handles focus on dismissal: https://jsfiddle.net/eo8x3pcu/show.

Results

Screen Reader/AT Browser Tab Support Virtual cursor/browse mode Context change feedback
NVDA Firefox Yes Yes No
NVDA Chrome Yes No No
NVDA Edge Yes No No
JAWS All Yes No No
Narrator Edge Yes No No
Narrator Edge (pre-Chromium) No Yes No
VoiceOver iOS Safari - No No
VoiceOver macOS Safari Yes Yes Yes
Talkback Chrome - No No
ZoomText All Yes N/A No

Detailed Notes:

As is (hopefully) clear, there is absolutely no consistency in how screen readers handle lost focus. At worst, the user gets no feedback about the change of context on close, and then the screen reader starts over from the top of the page. ZoomText (and potentially other assistive tech that wasn't tested) is also affected, with the screen failing to re-center somewhere useful to the user.

Scott O'hara also has support results for testing skip link focus and virtual cursor.

Solutions

The fix is easy: always manage focus when you remove the active element from the DOM!

Supplemental links:


sequential focus navigation starting point
sequential focus navigation starting point
sequential focus navigation starting point
sequential foction navigatus starting point
sequential focusing navigation start point
sequential focal navigation startus point
sequential fo-cat nappigation flop point

kitten flopped over, face down, sleeping