NVDA bug: anchor links broken if they point to a parent element
posted on
In my last blog post, I explained how to create a back-to-top button that visually indicates the scroll position. I tested it in different browsers to see if the CSS works. I didn't test with screen readers because the HTML was basic and straightforward. There wasn't anything that could go wrong, or at least I thought so.
<main id="content">
<p>…</p>
<a href="#content" class="back-to-top">
Back to top
</a>
</main>
<footer>
</footer>
I posted the link to the blog post on Mastodon, and James Scholes replied, telling me that NVDA doesn't respond to activating the link in Chrome. The browse mode cursor just stayed at the bottom of the page. Curtis Wilcox suggested that it may have something to do with the link being a child of the targeted element. I tested it, and he was right. When you use the cursor in NVDA and click an anchor link that points to a parent element, NVDA doesn't respond. It works fine if you use the Tab key. It also works fine if the link is outside the target. It's the same in Chrome and Firefox. I also tested it with JAWS in Chrome and VoiceOver with Safari, and there it works fine.
I filed a bug. As it turns out, that's a feature, as James Teh explains:
This fails because the position of the cursor and the scroll target (which is the entire container) overlap. NVDA doesn't report a scroll if the caret is already within the scroll target, I assume because that could result in double speaking and snapping of the cursor back to the scroll target if the scrolling event fires again. That said, this event really shouldn't fire multiple times. That behaviour was originally implemented back in February 2008 in 4ec0dd8. So maybe we can loosen this restriction. I'd suggest we check whether the caret overlaps the first line of the scroll target instead, which should be fairly straightforward to implement.
What does that mean for you? If possible, move anchor links outside their target until they fix the bug.
<main id="content">
<p>…</p>
</main>
<footer>
<a href="#content" class="back-to-top">
Back to top
</a>
</footer>