How we fixed dialogs from being read incorrectly in JAWS

In textbox.io dialogs we had a problem with the word count dialog. In particular what was happening is that JAWS would read the whole dialog and then start reading the rest of the document, like if the dialog was not blocking the UI.

The code looked like the following:


<div aria-describedby="ephox-aria_364273060451471304240619" aria-label="Word Count" aria-labelledby="ephox-aria_766513384441471304240617" class=
"ephox-polish-dialog ephox-polish-dialog-wordcount ephox-polish-dialog-narrow" role="dialog" tabindex="-1">
  <div class="ephox-polish-top">
    <span class="ephox-polish-dialog-title" id="ephox-aria_766513384441471304240617">Word Count</span><span aria-label="Cancel Dialog" class=
    "ephox-polish-dialog-close" role="button" tabindex="-1"></span>
  </div>
  <div class="ephox-polish-dialog-content" id="ephox-aria_364273060451471304240619">
    <table class="ephox-polish-tabular ephox-polish-wordcount-table">
      <thead>
        <tr>
          <th scope="col">Count&nbsp;</th>
          <th scope="col">Document&nbsp;</th>
          <th scope="col">Selection&nbsp;</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Words&nbsp;</td>
          <td class="ephox-polish-wordcount-document-words">402&nbsp;</td>
          <td class="ephox-polish-wordcount-selection-words">0&nbsp;</td>
        </tr>
        <tr>
          <td>Characters (no spaces)&nbsp;</td>
          <td class="ephox-polish-wordcount-document-charsnospaces">2486&nbsp;</td>
          <td class="ephox-polish-wordcount-selection-charsnospaces">0&nbsp;</td>
        </tr>
        <tr>
          <td>Characters&nbsp;</td>
          <td class="ephox-polish-wordcount-document-chars">2994&nbsp;</td>
          <td class="ephox-polish-wordcount-selection-chars">0&nbsp;</td>
        </tr>
      </tbody>
    </table>
  </div>
  <div class="ephox-polish-dialog-controls"></div>
</div>

Also, in the JS we were focusing the table and adding a key handler for keyboard navigation. This would be picked by AInspector, the tool we use for testing if all the attributes are set normally. The reason why AInspector was complaining it is that the table has a role by default as a data-table and it doesn’t expect any key handler. We removed the key handler from the table and left only the one on the outer dialog, to keep actions such as ESC to close the dialog.

However at this point JAWS would not read the dialog fine anymore: the dialog would result in being read twice, and once the reading would be done it would start reading the rest of the document. In our case, it would start reading the editor toolbar.

In the past, we had a data-table with roles and attributes, e.g:


<table role="grid"><tr role="row"><td role="gridcell"></td></tr></table>

Which is incorrect since the table is already a grid by default. To be semantically correct we tried then to convert the table to a table made of divs, obtaining a table like:


  <div role="grid" class="ephox-polish-tabular-table ephox-polish-tabular ephox-polish-wordcount-table">
    <div class="ephox-polish-tabular-group">
        <div class="ephox-polish-tabular-row" role="row">
            <span class="ephox-polish-tabular-cell" scope="col"> ${headerCounts}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" scope="col"> ${headerDocument}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" scope="col"> ${headerSelection}&nbsp;</span>
        </div>
    </div>
    <div class="ephox-polish-tabular-group">
        <div class="ephox-polish-tabular-row" role="row">
            <span class="ephox-polish-tabular-cell">${wordCountLabel}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-document-words">${documentCountWords}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-selection-words">${selectionCountWords}&nbsp;</span>
        </div>
        <div class="ephox-polish-tabular-row" role="row">
            <span class="ephox-polish-tabular-cell">${wordCountCharsNoSpacesLabel}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-document-charsnospaces">${documentCharactersNoSpaces}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-selection-charsnospaces">${selectionCharactersNoSpaces}&nbsp;</span>
        </div>
        <div class="ephox-polish-tabular-row" role="row">
            <span class="ephox-polish-tabular-cell">${wordcountCharsLabel}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-document-chars">${documentCountCharsWithSpaces}&nbsp;</span>
            <span class="ephox-polish-tabular-cell" class="ephox-polish-wordcount-selection-chars">${selectionCountCharsWithSpaces}&nbsp;</span>
        </div>
    </div>
</div>

And styled as:


.ephox-polish-tabular-table {
  display: table;
  width: auto;
}

.ephox-polish-editor-container .ephox-polish-tabular-row {
  display: table-row;
  width: auto;
}

.ephox-polish-tabular-heading-cell,
.ephox-polish-tabular-cell {
  display: table-cell;
}

This way we would obtain the behaviour we wanted, we could put a key handler on the table, but then, AInspector wouldn’t see the table as a data-table anymore, therefore cells with values of 0, which are simply fine in a regular table are not seen as “unique”, therefore we had to find a better way to deal with it.

Next step in the resolution of this problem was having the div structure being able to be focused, so we have:

  • Removed the key handler
  • Added a tabindex=”-1″
  • Focused the table

Result: nothing. JAWS would still read the whole table and then the content, so scrap that.

Next thing we tried has been to focus the “Cancel button”, to try stopping JAWS from reading the outer content while still reading the table contents.
Result: JAWS would just read the title of the dialog and announce the close button.

At this point since it is stopping at the cancel button, we might just add a button at the bottom of the dialog, such as an ‘OK’ button, which action would simply to close the dialog.
Result: JAWS would just read the button once again.

At this point we had an illumination:

  • Set a tabindex=”-1″ on a real data-table
  • Focus the table when the dialog opens without putting any keyhandler, while leaving the outer one for the dialog

The markup of the table would look like the following:


<table class="ephox-polish-tabular ephox-polish-wordcount-table" tabindex="-1">
    <thead>
        <tr>
            <th scope="col"> ${headerCounts}&nbsp;</th>
            <th scope="col"> ${headerDocument}&nbsp;</th>
            <th scope="col"> ${headerSelection}&nbsp;</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>${wordCountLabel}&nbsp;</td>
            <td class="ephox-polish-wordcount-document-words">${documentCountWords}&nbsp;</td>
            <td class="ephox-polish-wordcount-selection-words">${selectionCountWords}&nbsp;</td>
        </tr>
        <tr>
            <td>${wordCountCharsNoSpacesLabel}&nbsp;</td>
            <td class="ephox-polish-wordcount-document-charsnospaces">${documentCharactersNoSpaces}&nbsp;</td>
            <td class="ephox-polish-wordcount-selection-charsnospaces">${selectionCharactersNoSpaces}&nbsp;</td>
        </tr>
        <tr>
            <td>${wordcountCharsLabel}&nbsp;</td>
            <td class="ephox-polish-wordcount-document-chars">${documentCountCharsWithSpaces}&nbsp;</td>
            <td class="ephox-polish-wordcount-selection-chars">${selectionCountCharsWithSpaces}&nbsp;</td>
        </tr>
    </tbody>
</table>

And this has actually made AInspector happy, as we were using an actual data-table, without useless attributes. Also, JAWS started reading the table just fine stopping at the end of the dialog.

Hope this will be helpful for someone!