What determines where the focus goes back to when clicking on a browser's back button?
Asked Answered
L

1

7

As far as I can tell, it is in part determined by the HTML spec's History API1, specifically the value of history.scrollRestoration. Quoting the HTML spec's scroll restoration mode paragraph:

  • "auto"
    The user agent is responsible for restoring the scroll position upon navigation.

  • "manual"
    The page is responsible for restoring the scroll position and the user agent does not attempt to do so automatically.

This answer2 illustrates how the "manual" option can be used, but what about "auto"? Is the behavior then set by user agent implementations? If this is true, is my assumption correct that one would have to take the "manual" route to ensure that the back button's behavior is deterministic across browsers? (If they properly implemented the History API1, that is.) The example in the "Context" section below seems to confirm this.

[1]: HTML spec: 7.2 APIs related to navigation and session history > 7.2.5 The History interface; 7.4 Navigation and session history
       MDN: History API

[2]: The threads I have found in this topic (1, 2, 3) show activity from around 2014 and / or provide an answer without much explanation or reference to sources.


Context

We are trying to troubleshoot a page of legacy web application that is mostly used by screen reader users (via JAWS), but the behavior described below are the same for all users regardless:

  1. Click on a client link in a long <table> of rows along the lines of:

    Assignment Date Client Notes Instructor Assigned By Status
    April 12, 2024 Doe, John blabla Goodworker, Sally Bigboss, Paige Assigned
  2. On the client's page, the user clicks on a button; such as:

    <input type="button"
           aria-label="Plans"
           onclick="location.href='/plans/1234';"
           value="Plans"
    >
    
  3. Click on the browser's back button.

    Focus:

    • JAWS: the "Plan" button in step 2.
    • Chrome3: <body>, but when hitting TAB, the focus jumps to the very first link, and not to the button clicked previously.
    • Firefox4,5: the "Plan" button in step 2.
  4. Click on the browser's back button.

    Focus:

    • JAWS: <body>; on TAB, focus jumps back to link used to navigate away.
    • Chrome3: <body>; on TAB, focus jumps to link in the next line.
    • Firefox4,5: the link used to navigate away from the page in step 1.

[3]: Chrome DevTools: Track element focus

[4]: Determined by issuing document.activeElement in the dev console after each back button press.

[5]: For the record, Firefox does retain scroll information in this page in our web app, but when I tested this MDN page, the results were inconsistent. For example, search for "single-page application", the first result is a link, clicking on it, then going back shows the anchor as focused. There is a link right below saying fetch(), but clicking and going back will show that the focus is on <body>...


update:

Leekgreen answered 13/4 at 2:1 Comment(5)
What an impressively well-written question.Barbicel
Is there a reason that a button input element with inline javascript is being used to redirect the page instead of using a link? Using an <a> element seems like it would be more appropriate and might produce more consistent back button results across user agents.Scrouge
@Scrouge Good catch! I don't think there were any deep thoughts behind taking this route. It's a legacy app held together wishful thinking, so I try not to touch anything. In this case though, it seems to be useful for JAWS: according to users, JAWS reports this button as the active element, but not the link on the first page of the history. Will have to test this, but JAWS documentation is almost non-existent (couldn't even figure out if it has its own user agent implementation), and this could be a bug - but then, it is a proprietary software with no transparent bug reporting.Leekgreen
Impressively thorough research! Does the affirmation of this being a bug answer the question for you? Does it mean it should be the browser, and Firefox does it correctly?Yankeeism
@Yankeeism Thank you! The Chrome bug is only part of the answer - and Firefox's behaviour is also inconsistent (so will probably try to report it too). I already have an answer outline with lots of to-dos, but didn't have the time to round it out. I decided to post it as is when time permits, and update it as I figure things out.Leekgreen
L
1

Short answer

The HTML specification only addresses scroll position restoration behavior, so the main factors that influences where focus will land when navigating the browser history are:

  • user agent implementations
  • JavaScript
  • accessibility attributes
  • browser extensions

At the moment, the only solution to make this behaviour consistent seems to be using JavaScript to restore focus manually (see section 3.). (TODO: are there other ways?)



Long answer

The statement above is based on my understanding of the 7.4 Navigation and session history section of the HTML spec. (The very first paragraph is sobering).

ASIDE: scroll position vs focus(ed element)

Understanding the difference is important because scroll position is not useful for screen readers. This seems to be the gist:

  • Scroll position is the visible part of a web page and is independent on where the focus is.1

  • Focused element is an HTML element that a user selected using the TAB key on the keyboard (i.e., "tabbable").2

[1]: It is way more complex than this; I started here, followed the "reading advice", but gave up after more than twenty open tabs (the modern equivalent of the Fighting Fantasy book's 1..2..23 finger-placeholders rule).

[2]: A sloppy way to put it, but seems to boil down to this, and it is especially valid when looking from an accessibility viewpoint. In the context of this question, focused elements are also interactive elements that modify the navigation history and not elements that were made "tabbable" (see web.dev's Focus article)3.

[3]: HTMX can make any element an interactive one, but they won't become "tabbable" as far as I know. HTMX's hx-push-url attribute helps with navigation history consistency, but getting if focused will probably require considerable scripting effort.


1. Influencing factors

1.1 User agent implementations

None of the major browser implementations have formal specifications (or I couldn't find them), and the only way to answer this question is to look at the code (if possible) or ask.

TODO: Are there user agents where this behavious is configurable? (e.g., settings, compile flags)

  • Chrome

    The focus is not restored after going back; see Chromium issue.

  • Edge

    Same as Chrome (after all, it is Chromium-based, if I remember it correctly).

  • Firefox

    Inconsistent behaviour; see Firefox bug report.

  • Safari

    Same as Chrome; filed a report on https://www.apple.com/feedback/safari.html (which is not public) and not on the WebKit site because I'm very unfamiliar with Safari / WebKit, not to mention their relationship.

1.2 JavaScript

Web pages with may have scripts embedded that manipulate focus.

1.3 HTML attributes

The MDN article on the autofocus global attribute already addresses its potential issues with screen readers. ARIA also has extra roles and attributes to improve accessibility (some that also affect focus), but they tend to be applied incorrectly.

1.4 Browser extensions

The same argument applies as under section "1.2 JavaScript" above.


2. Screen readers

Decided to create a separate section for screen readers, because I know little to nothing about how they work. For example, are they considered user agents or do they rely solely on existing browsers?

I presume it is the latter, because otherwise users wouldn't have to use Chrome, Firefox, etc., but screen readers do seem to have an internal mechanism to smooth over differences in user agent implementations:

2.1 JAWS

A Chrome + JAWS user reported that when navigating back, JAWS will announce the focus to be on <body>, but when pressing TAB, the focus will jump to the next "tabbable" element right after the one that was used to navigate away initially.

For example:

  1. User clicks on link "Doe, John" in a table row on the page.

    Assignment Date Client Notes Instructor Assigned By Status
    April 12, 2024 ->Doe, John<- blabla Goodworker, Sally Bigboss, Paige Assigned
  2. Press   ALT+   to go back to the previous page.

  3. Press TAB, and the focus is reported to be on the link "Goodworker, Sally":

    Assignment Date Client Notes Instructor Assigned By Status
    April 12, 2024 Doe, John blabla ->Goodworker, Sally<- Bigboss, Paige Assigned

TODO: Report this behaviour to Freedom Scientific, because the previous focus target was obviously saved by JAWS, but it is unintuitive, especially for users new to JAWS and/or web browsing.

TODO: Test JAWS with other user agents as well.

2.2 NVDA

2.3 Windows Narrator

2.4 Apple VoiceOver

TODO: Finish sections above by testing their behaviour the same way as with JAWS.

TODO: List other screen readers, however niche. (See 1, 2, 3, for example.)


3. Make behaviour consistent using JavaScript

TODO: These notes are little more than guesswork.

  • For existing websites: browser extensions?..

  • When developing a new web app:

    One implementation is by adding event listeners to:

    1. Interactive elements to store their reference when clicked
    2. window to restore focus when navigating back in history.

    This snippet will have to be tested:

    function storeFocus() {
        sessionStorage.setItem('focusedElementId', document.activeElement.id);
    }
    
    function restoreFocus() {
        const focusedElementId = sessionStorage.getItem('focusedElementId');
        if (focusedElementId) {
            const focusedElement = document.getElementById(focusedElementId);
            if (focusedElement) { focusedElement.focus(); }
        }
    }
    
    window.addEventListener('popstate', function(event) {
        restoreFocus();
    });
    
    document.getElementsByTagName('a').addEventListener('click', function() {
        storeFocus();
        window.location.href = 'another-page.html';
    });
    
Leekgreen answered 21/4 at 17:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.