Aria-live doesn't say label after updates

3k views Asked by At

I have this piece of HTML which is updated dynamically with JS. The screen reader only reads out the new value when they get updated. It doesn't say the label of the input who was updated.

<ul class="points-transfer-detail-points-calculation clearfix">
    <li>
        <label for="points-to-transfer">{{{ pointsToTransferLabel }}}</label>
        <input id="points-to-transfer" type="text" aria-controls="brand-points points-left-after-transfer" placeholder="XXX,XXX" {{#if disabled }}disabled{{/if}}>
        <p id="points-to-transfer-error" class="points-transfer-detail-form-error" aria-hidden="true" role="alert">{{{ pointsToTransferErrorMessage }}}</p>
    </li>
    <li>
        <label for="brand-points">{{{ brandPointsLabel }}}</label>
        <input id="brand-points" type="text" aria-live="polite" aria-atomic="true" disabled>
    </li>
    <li>
        <label for="points-left-after-transfer">{{{ pointsLeftLabel }}}</label>
        <input id="points-left-after-transfer" type="text" aria-live="polite" aria-atomic="true" disabled>
    </li>
</ul>

I have tried to use aria-labelledby, aria-describedby, role="alert" and aria-label but no results, only the value of the input and never his label.

From all my research on Google and StackOverflow, I didn't manage to found a proper answer.

I'm using NVDA in Firefox as a screen reader.

Thank you for your help.

1

There are 1 answers

1
haltersweb On BEST ANSWER

The only time a label should be read by a screen-reader is when focus is placed on its corresponding field.

Your input fields are all disabled. Therefore the labels wouldn't be read since you can't focus into the fields.

Remove your aria-live and aria-atomic from your input fields. They are unusable on input fields. Aria-live is triggered on DOM change within the container it's assigned to. An input field is not a container. Also, labels shouldn't be announced that way anyway.

If you want to announce a change to the DOM I would suggest injecting content into an empty aria-live div at the bottom of your page and hide it accessibly.

Here is a working example with one static label and 3 dynamic labels. One uses the "disabled" attribute, and one uses aria-disabled so that it can still receive focus. An announcement about the rendering of the new labels is also featured using an accessibly-hidden aria-live container.

This has been tested in NVDA in FF, JAWS in IE, and VO in Safari.

(function () {
  function populateLabels () {
    document.querySelector('[for="dogsName"]').appendChild(document.createTextNode('Dog\'s Name'));
    document.querySelector('[for="catsName"]').appendChild(document.createTextNode('Cat\'s Name'));
    document.querySelector('[for="lastName"]').appendChild(document.createTextNode('Last Name'));
  }
  function announceChange () {
    var announcement = "Some new labels have appeared.  They are Last Name, Dog's Name, and Cat's Name.",
        ariaLiveContainer = document.querySelector('[aria-live]');
    ariaLiveContainer.appendChild(document.createTextNode(announcement));
    setTimeout(function () {
      ariaLiveContainer.innerHTML("");
    }, 2000);
  }
  setTimeout(function () {
    populateLabels();
    announceChange();
  }, 3000);
}());
input {
  border: 1px solid black;
}
[disabled],
[aria-disabled="true"] {
  border: 1px solid #ccc;
  background-color: #eee;
}
.acc-hidden { /* Hide only visually, but have it available for screenreaders */
  position: absolute !important;
  display: block;
  visibility: visible;
  overflow: hidden;
  width: 1px;
  height: 1px;
  margin: -1px;
  border: 0;
  padding: 0;
  clip: rect(0 0 0 0);
}
<p>The first label is there on DOM load.  The other three labels come in 3 seconds after DOM load.  An announcement is made about the updated labels.</p>
<form action="">
  <ul>
    <li>
      <label for="firstName">First Name</label>
      <input type="text" name="first-name" id="firstName" />
    </li>
    <li>
      <label for="lastName"></label>
      <input type="text" name="last-name" id="lastName" />
    </li>
    <li>
      <label for="dogsName"></label>
      <input type="text" name="dogs-name" id="dogsName" disabled /> (uses the disabled attribute -- doesn't receive focus)
    </li>
    <li>
      <label for="catsName"></label>
      <input type="text" name="cats-name" id="catsName" aria-disabled="true" /> (uses the aria-disabled="true" attribute -- can receive focus)
    </li>
  </ul>
</form>
<div class="acc-hidden" aria-live="polite"></div>