Search code examples
htmlgoogle-chromewai-arianvda

NVDA screenreader not reading alert message with latest Chrome version


I have created below HTML demonstration page. In that page a message is added to a div when selecting the "No" radiobutton. The div has aria-tags on it. The div in question has ID "lblErr" / "lblErr2".

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Screenreader Errormessage Test with Radiobuttons</title>
    </head>
    <body>
        <label id="instructionLbl">
            Please select below
        </label>
        <div>
            <div id="firstItemGroup">
                <label id="headerTextLbl">
                    Select no to generate the error message
                </label>
                <fieldset aria-labelledby="headerTextLbl">
                    <div id="selectionGroup1">
                        <table id="radioBtnTable">
                            <tbody>
                                <tr>
                                    <td>
                                        <label for="button:conf:1">Yes</label>
                                        <div class="ui-radiobutton ui-widget">
                                            <div class="ui-helper-hidden-accessible">
                                                <input tabindex="1" id="button:conf:1" name="button:conf" type="radio" value="false" onchange="var errorDiv = document.getElementById('lblErr');while(errorDiv.hasChildNodes()){errorDiv.removeChild(errorDiv.lastChild);}">
                                                </input>
                                            </div>
                                        </div>
                                    </td>
                                    <td>
                                        <label for="button:conf:2">No</label>
                                        <div class="ui-radiobutton ui-widget">
                                            <div class="ui-helper-hidden-accessible">
                                                <input tabindex="2" id="button:conf:2" name="button:conf" type="radio" value="false" onchange="var errorDiv = document.getElementById('lblErr');errorDiv.style.display = 'block';var newDiv = document.createElement('div');var spanOne = document.createElement('span');spanOne.classList.add('ui-message-error-icon');var spanTwo = document.createElement('span');spanTwo.classList.add('ui-message-error-detail');var errMsg = document.createTextNode('Very long error message with special red font, details, paragraphs and recovery suggestion is displayed here');spanTwo.appendChild(errMsg);newDiv.appendChild(spanOne);newDiv.appendChild(spanTwo);errorDiv.appendChild(newDiv);">
                                                </input>
                                            </div>
                                        </div>   
                                    </td>
                                </tr>
                            </tbody>
                        </table>     
                        <div id="lblErr" style="display: none;" role="alert" aria-atomic="true" aria-live="polite" tabindex="3"></div>
                    </div>
                </fieldset>
            </div>
            <div id="subItemsGroup">
                <label id="headerTextLbl2">
                    Label with headertext
                </label>
                <fieldset aria-labelledby="headerTextLbl2">
                    <div id="selectionGroup2">
                        <label id="instructionLbl2">Select no to generate the error message</label>
                        <table id="radioBtnTable2">
                            <tbody>
                                <tr>
                                    <td>
                                        <label for="button:conf:3">Yes</label>
                                        <div class="ui-radiobutton ui-widget">
                                            <div class="ui-helper-hidden-accessible">
                                                <input tabindex="4" id="button:conf:3" name="button:conf:2" type="radio" value="false" onchange="var errorDiv = document.getElementById('lblErr2');while(errorDiv.hasChildNodes()){errorDiv.removeChild(errorDiv.lastChild);}">
                                                </input>
                                            </div>
                                        </div>
                                    </td>
                                    <td>
                                        <label for="button:conf:4">No</label>
                                        <div class="ui-radiobutton ui-widget">
                                            <div class="ui-helper-hidden-accessible">
                                                <input tabindex="5" id="button:conf:4" name="button:conf:2" type="radio" value="false" onchange="var errorDiv = document.getElementById('lblErr2');errorDiv.style.display = 'block';var newDiv = document.createElement('div');var spanOne = document.createElement('span');spanOne.classList.add('ui-message-error-icon');var spanTwo = document.createElement('span');spanTwo.classList.add('ui-message-error-detail');var errMsg = document.createTextNode('Very long error message with special red font, details, paragraphs and recovery suggestion is displayed here');spanTwo.appendChild(errMsg);newDiv.appendChild(spanOne);newDiv.appendChild(spanTwo);errorDiv.appendChild(newDiv);">
                                                </input>
                                            </div>
                                        </div>   
                                    </td>
                                </tr>
                            </tbody>
                        </table>     
                        <div id="lblErr2" style="display: none;" role="alert" aria-atomic="true" aria-live="polite" tabindex="6"></div>
                    </div> 
                </fieldset> 
            </div>
        </div>
    </body>
</html>

The JavaScript code which adds the error message. I added it to the onChange of the radio buttons:

var errorDiv = document.getElementById('lblErr');
var newDiv = document.createElement('div');

var spanOne = document.createElement('span');
spanOne.classList.add('ui-message-error-icon');

var spanTwo = document.createElement('span');
spanTwo.classList.add('ui-message-error-detail');

var errMsg = document.createTextNode('Very long error message with special red font, details, paragraphs and recovery suggestion is displayed here');

spanTwo.appendChild(errMsg);
newDiv.appendChild(spanOne);
newDiv.appendChild(spanTwo);
errorDiv.appendChild(newDiv);

This is an extracted and simplified version of our production HTML / JS. In production there are 5 to 6 of these selections one below the other and it is embedded into a greater HTML structure (with multiple parent divs which also have navigation elements and other stuff).

This HTML has a problem in combination with Google Chrome and an NVDA screen reader. In production when selecting the "No" selection the error message is added to the HTML, but no error message is read at all by NVDA. The same case in Firefox causes NVDA to read "alert ...".

The following section has been edited, please see edit below.

This reduced demonstration snippet behaves slightly different. Here Firefox reads the error message twice. Once only the messagetext and then the second time "alert" and then the message text. Chrome reads it only once and omits the second message starting with "alert". I only want it to read "alert ..." in both browsers.

The span and div construction surrounding the error messages is the same in production and in my snippet.

My question is: Why does NVDA not read "alert ..." when the message is added in Chrome? Is this a problem with the HTML or Chrome or NVDA? Can I fix it by making a non-breaking change to the HTML?

Edit: I now edited the HTML / JS so that it does not read any error message in Chrome at all, while it still reads it in Firefox. What changed is that the divs lblErr/lblErr2 now start out with a "display: none" style attribute, even though I remove it, before adding the error message. So it appears that Chrome / NVDA ignores modifications to divs, which start out with "display: none". Is this according to specification or is this a bug in Chrome?


Solution

  • As I already mentioned in the edit section of my question, the core problem was that the div where I add the error message to with JavaScript has an initial display style attribute set to "none". It is also initially empty.

    <div id="lblErr" style="display: none;" role="alert" aria-atomic="true" aria-live="polite" tabindex="3"></div>

    With this setup Google Chrome + NVDA does not read the dynamically added error message.

    Once I remove the initial display style or change it to something else Google Chrome + NVDA starts reading the dynamically added error message.