Skip to content

Second thoughts on focus management #202

@domenic

Description

@domenic

The current explainer draft describes a new default focus behavior that single-page apps (SPAs) can opt into, by using the new navigation API. Translating the technical details there into an overview:

By default, any [SPA nav that goes through the new navigation API] will cause focus to reset to the <body> element, or to the first element with the autofocus="" attribute set (if there is one). This focus reset will happen after [the developer indicates the navigation has finished]. However, this focus reset will not take place if the user or developer has manually changed focus [during the developer-indicated loading interval]

This default behavior is called the "after-transition" focus reset behavior.

This was motivated by this post by @marcysutton, which states

... a user’s keyboard focus point may be kept in the same place as where they clicked, which isn’t intuitive. In layouts where the page changes partially to include a deep-linked modal dialog or other view layer, a user’s focus point could be left in an entirely wrong spot on the page.

and by the desire to make the "new default" for SPA navs the same as how MPA navs behave: i.e., to reset focus. The reasoning being, resetting focus to the body (or designated element with autofocus="") would ensure none of the bad outcomes Marcy describes would occur.

However, upon testing some SPAs in the wild, I'm having second thoughts about this. Essentially, resetting the focus by default is hostile to "tab" type patterns. For example:

  • This Google search displays a carousel of the moons of Jupiter. Clicking on any of them performs an SPA nav. Focus stays on the clicked moon. Resetting focus to the body would be bad here.

  • twitter.com's logged-in view displays tabs along the sideline. Clicking on, e.g., "Profile", performs an SPA nav. And focus stays on the "Profile" link. Resetting focus to the body would be bad here.

  • Airbnb's experiences page (sample) has various filters, e.g. "Arts and culture", "Sports", "Tours". Clicking on these performs an SPA navigation (and updates the very end of the URL bar). Focus remains on the thing you clicked. Resetting focus to the body would be bad here.

I think there are many more examples.

So now I'm not sure what to do. Some ideas:

  • Stick with the current plan, and make people aware that these sorts of tab patterns will need to use the navigation API's { focusReset: "manual" } option.

  • Change the default focus behavior for navigation API SPA navs, to "manual": which means, don't touch focus. The usual focus fixup rule will apply if you remove the currently-focused element from the DOM or make it inert or hide it with display: none. But if you translate it offscreen, or hide it behind something, or use visibility: hidden, then it will stay focused, with potentially confusing consequences for users. You can opt in to the no-longer-default reset behavior by using { focusReset: "after-transition" }.

  • Slight tweak of the "manual"-as-default plan above, but with a modified version of the focus fixup rule where it also accounts for autofocus="". Essentially, have a navigation API SPA nav reset the "has autofocus happened yet?" flag, allowing easy customizability of what gets focused after whole-page-replacing SPA navs.

  • A bigger tweak of the above, where we have some sort of "super focus fixup rule" which runs after navigation API SPA navs. E.g.: if the currently-focused element is not intersecting the viewport, or is occluded in some way, then reset focus. This seems scary because getting just the right amount of magic seems difficult.

  • Have the cleverness be based on the specific case where clicking an <a> causes a navigation API SPA nav. If that particular <a> is still in the DOM and focusable after the SPA nav finishes, then don't reset focus. Otherwise, reset focus. (To be clear, this would reset focus in cases like <button onclick="navigation.navigate('foo')">. Only <a>s get special treatment; tracking navigations through arbitrary JavaScript is too much magic.)

The most important question right now is what the right default should be. We will always have a "manual" mode available. And if we think that some of these clever modes are useful and people might want them in some cases, we can always implement them later. But we need to figure out the default---whether it's "manual", "after-transition", or something more clever---before we can ship an initial version of the navigation API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions