jennEDVT jennEDVT - 6 days ago 6
Javascript Question

NVDA screen reader not switching to focus mode predictably

I have a simple chunk of code that I'm trying to make keyboard accessible while using the NVDA screen reader.

Specifically, I have a div with a role of "button", with another div with another role of "button" nested inside it. Each div has a different onkeydown event that gets fired when the user tabs to that div, and presses "enter".

This keyboard functionality all works as desired when I don't have the NVDA screen reader turned on.

When I do have the screen reader turned on, however, the nested keydown event no longer fires. Instead, just the parent event fires even when the nested event is one that has focus.

However, if I manually change NVDA out of "browse mode" and into "focus mode" (By pressing the NVDA key + spacebar), then the key events work as desired again.

Unfortunately, it's not acceptable for me to expect someone using NVDA to know to make the manual switch to "focus mode". It either needs to change to "focus mode" automatically, or it needs to work in "browse mode."

Here's the code:

HTML:

<div role="button"
tabindex="1"
onkeydown="keyEvent(event, outerDivAction)"
class="outerDiv">
Outer Div

<div role="button"
tabindex="1"
onkeydown="keyEvent(event, innerDivAction)"
class="innderDiv">
Inner Div</div>
</div>

<div class="result"></div>


JavaScript:

function outerDivAction(event) {
event.stopPropagation();
console.log('outer div');
$('.result').html('<p>outer div!</p>');
}

function innerDivAction(event) {
event.stopPropagation();
console.log('inner div')
$('.result').html('<p>inner div!</p>');
}

function keyEvent(event, callback) {
event.stopPropagation();
if (event.which === 13) {
callback(event);
}
}

$('.outerDiv').click(outerDivAction);

$('.innderDiv').click(innerDivAction);


You can also view a codepen here: http://codepen.io/jennEDVT/pen/Yprova

Any help that anyone can offer would be greatly appreciated!

p.s.
I know that if I take the nested div and move it so that it's no longer nested, but is rather a sibling of the first div, then everything works as desired. Unfortunately that's not an option. The div needs to be nested.

Answer

This is not a bug in NVDA.

First of all, you cannot have nested clickable elements. Specifically, you cannot have nested interactive content. You cannot nest links and you cannot nest buttons. You cannot nest links in buttons, and you cannot nest buttons in links. There are other kinds of interactive content worth looking into for future reference too.

You may find that your code is technically valid, but that is only because what you have written is a fib.

Instead of using the correct element (<button>) you have chosen to put role=button on <div>s. An HTML validator would pass your code because it is valid to nest <div>.

However, by giving them each role=button, you have instructed the user agent to treat them as <button> (minus all the benefits that come with them, like accessibility, key handlers, semantics, etc.).

Now let's go back and validate that code again as a user agent would see it (as nested <button>s). The W3C Nu HTML checker would fail it (I know because I ran a test):

Error: Start tag button seen but an element of the same type was already open.

My suggestion is:

  • to not nest them,
  • convert them to <button>s,
  • remove the invalid tabindex=1 (which you would not need),
  • remove the check for the key code as the <button> gives you that for free (including character 32),
  • make your <div class="result"> into an ARIA live region (there are some tips here),
  • and put a wrapper around the buttons to give you the visual effect you want.

Sample rejiggered code:

 <div class="wrapper">
   <button class="outerDiv">
     Outer Div
   </button>
   <button class="innderDiv">
     Inner Div
   </button>
 </div>

 <div class="result" role="alert" aria-live="assertive"></div>