When JAWS won't read form labels on IE

23.09.2016

TL;DR: Try removing role="form".

It took me 4 days to find out the above solution and, what makes me even less proud, I didn’t even do it myself! In the following article, I’d like to share the hard parts of fixing accessibility bugs.

The bug

I was given a defect, which could be generalized like this:

When moving through form fields with TAB key, at certain field JAWS starts reading from the top instead reading just the field’s label. This happens only on IE.

All the steps

At the beginning, it smelled like a trivial missing label (remember that they’re paired with fields by equal values of for/id attributes of respective tag). Imagine how shocked I was when JAWS List of Form Fields tool (INSERT+F5) showed all the fields perfectly labelled and even correctly read every single label on highlight. Still, after activating the problematic field, JAWS failed to announce it properly.

I also tried to “strengthen” the labelling by adding aria-label and aria-labelledby attributes here and there, but without success. Later I scanned the page again with AccChecker, just to confirm there was no problem with field/label pairing.

Then I blamed Angular.

The form lived in an application with routing, data binding and all the fine ng-goodies. The form made use of some custom directives, and it was easy to assume that maybe some JavaScript is messing with focus, tabbing order or keyboard events. I stripped all unnecessary attributes from the code, but unfortunately the buggy behavior was still there.

The next thing to check was structure of the whole page. The idea behind this was reasonable: if there was some unclosed tag, the focus could “leak”. Quick validation resulted in no markup errors.

At this point I was pretty frustrated. I really wanted to know what’s wrong with the JAWS + IE, but ran out of ideas. Out of suspicion, I put together this basic, clean form:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Test form</title>
  </head>
  <body>
    <form role="form" name="form" id="form" novalidate method="post" action="jaws-form.html">
      <h2>Test form</h2>
      <div>
        <label for="amount">I want to borrow</label>
        <input id="amount" name="amount" type="number">
      </div>
      <div>			
        <label for="years">Over a term of years</label>
        <select id="years" name="years">
          <option value="a">Option a</option>
          <option value="b">Option b</option>
          <option value="c">Option c</option>
        </select>
      </div>
      <div>
        <label for="months">Over a term of months</label>
        <select id="months" name="months">
          <option value="x">Option x</option>
          <option value="y">Option y</option>
          <option value="z">Option z</option>
        </select>
      </div>
      <div>
        <label for="purpose">What is the purpose of this loan?</label>
        <select id="purpose" name="purpose">
          <option value="fickcar">Fick car</option>
          <option value="buckcar">Buck car</option>
        </select>
      </div>
      <div>
        <label for="day">What day of the month should we take your loan repayment?</label>
        <select id="day" name="day">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
      </div>
      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  </body>
</html>

Looks innocent, right? Sadly, when TAB-bed to the last field JAWS fails to read its label and instead starts reading from the top!

What’s even weirder, this behavior can be fixed by applying random changes like:

WTF? I’d like the control over my form, please. I don’t want to shuffle the content and structure until I find single combination that satisfies the screen reader!

Then my colleague wrote to me:

What if you removed that role=”form”?

The very last thing I haven’t tried. Because I believed it was necessary. Because Bootstrap used to use this markup in their docs.

And boom. It started working. On IE. With the original form. Regardless all the ng-additions.

What I know now