nvda icon indicating copy to clipboard operation
nvda copied to clipboard

NVDA not reading focused parent

Open haltersweb opened this issue 7 years ago • 16 comments

I have an actionable element nested inside of a div with tabindex="-1". When the actionable element is clicked, focus is placed on this parent div. Yet, the div's content is not read by the NVDA screen reader:

  • NVDA Result (in both IE and FF): when I click the actionable element, focus goes to the div but the div's contents are not read.
  • JAWS Result (in both IE and FF): when I click the actionable element, focus goes to the div and the div's contents are read.

But, if the actionable element is outside of the target div, when the div receives focus the contents are read properly.

Here is a code-pen to test with:

  • Full-page version: http://codepen.io/haltersweb/full/YpEWov
  • Page with supporting code: http://codepen.io/haltersweb/pen/YpEWov

haltersweb avatar Nov 30 '16 18:11 haltersweb

P3 because I can certainly see why this is problematic, but it's extremely difficult to fix. It's possible that a fix for #2039 might fix this.

At present, NVDA ignores focus on an ancestor in this case because the browse mode cursor is already "within" the focused element. The reason for this is that NVDA itself moves the focus when you move the browse mode cursor, but we don't want this focus change to cause spurious reading and to snap the browse mode cursor to the start of the element (which is normally the expected behaviour for focus). There's no reliable way of determining whether a focus change was caused by NVDA itself or by the web page, so we assume focus on an element which already contains the browse mode cursor was caused by NVDA.

jcsteh avatar Nov 30 '16 23:11 jcsteh

Where I see this being a big issue is with Single-page Applications (SPA).

Typically new content and the controls to advance throuch the SPA are populated into a common container div. So when you try to advance within the SPA through the controls within the container, focus is driven back to the parent container in order for the screen-reader to read all new content and controls.

haltersweb avatar Dec 01 '16 02:12 haltersweb

Here is a "real-world" application. It is an example of a Single Page Application, describing accessibility with SPA: http://haltersweb.github.io/Accessibility/spa.html

haltersweb avatar Mar 02 '17 20:03 haltersweb

I'm seeing this behavior, sometimes. Working on a React app that sets focus on the <main> with a ref triggered on componentDidMount(). NVDA reads out the focused section when I manually reload the page, but only starts reading about half the time when loaded via React Router. There doesn't seem to be any pattern; it seems focus is being too early, and NVDA doesn't realize content has changed and it should start reading.

1Copenut avatar Jan 18 '18 18:01 1Copenut

Could we get an update on this issue? I am working on multiple SPAs and this is causing some major issues. NVDA will not alert the user when new content is displayed. We are displaying new content by hiding our current Div and showing a new Div inside of our SPA container. We are explicitly setting focus on elements in our new content Div in the hopes that NVDA will read this off to the user and alert them a new page is loaded. NVDA doesn't read anything to the user, no matter what I focus on. The only thing that seems to get NVDA to respond is if there are form elements on the new content Div we load.

Below is a jsfiddle that shows the exact problem. When you are using only a keyboard, the new page heading that I focus to isn't read off by NVDA. https://jsfiddle.net/1wp6mc4w/13/

Again, this is a major issue causing real problems for SPA and this defect has been opened since 2016 with no appropriate response. #2039 is not the same issue that we are dealing with. Please respond to this defect with something of substance.

bobthecavemonkey avatar Apr 05 '18 14:04 bobthecavemonkey

We have the exact same issues as described in this thread. We have a single page application and in our specific case we have a login screen that switches to a loading screen upon submitting. In this loading screen we put focus on a message that is showing on the loading screen, but nothing is read out by NVDA. Huge problem as this affects our application in many different places.

The problem is very well described by @bobthecavemonkey and is can be reproduced in his jsfiddle.

Any suggestions on how to walk around this issue or perhaps solve it without re-doing a lot of stuff?

BenjaminGolba avatar Sep 05 '18 12:09 BenjaminGolba

Reopened as #8869 had to be reverted for now.

michaelDCurran avatar Nov 20 '18 22:11 michaelDCurran

I'd love to team up with someone on the NVDA side, as I've been working on this from the SPA perspective- specifically, trying to see if we can solve this in Ember.js.

I see that Ember uses the history API in JavaScript frameworks- an API that came after the implementation of screen readers. Specifically, history.pushState, while useful for quick navigation (among other things) seems to not trigger the type of event that NVDA seems to be watching for.

While JavaScript frameworks can implement some solutions to mitigate this (the Ember community in particular has a couple of different addon solutions and I'm trying to work on it in the core framework), it's entirely possible that the solution would be better handled in assistive technology.

Is there any way I could help? I'd like to be part of the solution if at all possible.

MelSumner avatar Dec 10 '18 19:12 MelSumner

I also have a similar issue when the focus goes from a button to a section or button that was hidden. The focus goes to the right element, but NVDA never read the content. I tested with chromeVox and it works.

https://codepen.io/jmnoeltink/pen/BbppOm

jmn90 avatar Mar 07 '19 17:03 jmn90

cc: @jcsteh

Adriani90 avatar Mar 07 '19 21:03 Adriani90

This is still reproducible in NVDA 2019.1 anf Firefox 66.0.1 as well as Google Chrome 73.

Adriani90 avatar Mar 25 '19 21:03 Adriani90

@haltersweb That's a good showcase app. We implemented your idea in a workaround in our React app. So, the workaround is to have an aria-live region outside of the current SPA "page" content. Example:

<div id="page-loading-status-announcement" role="status" aria-live="assertive"/>

<main>
  ...
</main>

And then on each subsequent navigation it should:

  • document.getElementById('page-loading-status-announcement').textContent = 'New page is being loaded' on before loading new page has started.
  • document.getElementById('page-loading-status-announcement').textContent = '' after loading new page has ended (but not rendered yet, for example) — clears the announcement status (just to keep things consistent).
  • Waits for the new page to render.
  • document.getElementById('page-loading-status-announcement').textContent = 'New page content has been loaded'
  • setTimeout(() => document.getElementById('page-loading-status-announcement').textContent = '', 100) — clears the announcement status for the next state machine cycle.
  • Focus the new page content if not already auto-focused something (for example, some "autofocused" input field): if (document.activeElement === document.body) { document.querySelector('main').focus() }.
#page-loading-status-announcement
  position: absolute
  z-index: -1
  opacity: 0

catamphetamine avatar Apr 19 '19 22:04 catamphetamine

Did anyone find some workaround? Currently I am working on some project (SPA) where I reproduced this issue.

MIKOLAJW197 avatar Sep 09 '19 11:09 MIKOLAJW197

Did anyone find the solution? Still facing the same issue. The element is getting focused but not read out by NVDA.

chintan97 avatar Feb 19 '20 18:02 chintan97

Seeing the same issue; NVDA will read and recognize child elements, but shift-tabbing into the parent element will cause NVDA to ignore it. Tabbing onto the same parent element from an element that exists previous to it works, however.

carsonpowers avatar Feb 28 '20 19:02 carsonpowers

I run into this issue all the time with aria-hidden="true" elements that aren't read when turned to aria-hidden="false" and are programmatically focused on, and here's what I find works for me:

  1. Set aria-hidden="false" to everything that should be aria-hidden="true"
  2. Once the page loads or your component "mounts", set those ones that should be hidden to aria-hidden="true"

Doing this allows me to trick NVDA into registering the elements when they are loaded, and then I subsequently hide them. It's a pretty awful workaround, but it's something.

rpearce avatar Sep 21 '22 21:09 rpearce