最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does my ARIA live region only announce the first update when using a Web Component? - Stack Overflow

programmeradmin1浏览0评论

I’m building a custom Web Component that displays live sustainability stats for hotels. The component updates CO₂ savings in real-time, and I want screen readers to announce the updates using an ARIA live region.

The problem is that the screen reader only announces the first update, but ignores all future changes unless I manually re-render the component.

Here’s my Web Component:

class LiveStats extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.innerHTML = `
      <div aria-live="polite" id="co2-output">CO₂ Saved: 10kg</div>
    `;
  }
  
  updateCO2(newValue) {
    const output = this.shadowRoot.getElementById("co2-output");
    output.textContent = `CO₂ Saved: ${newValue}kg`;
  }
}

customElements.define("live-stats", LiveStats);

I expected that calling updateCO2(15) would trigger a screen reader announcement for the new value.

I also tried forcing a reflow:

output.textContent = "";
setTimeout(() => {
  output.textContent = `CO₂ Saved: ${newValue}kg`;
}, 10);

But this doesn’t work reliably.

Are ARIA live regions inside Shadow DOM unreliable, or is there another technique to ensure screen readers consistently read dynamic updates?

UPDATE: The following is some dummy code. The page only has a single Web Component (live-stats) with a simple counter that updates every 3 seconds. The text "Updates: 0" is a placeholder—it doesn’t represent real-world data. The page doesn’t connect to a backend, API, or external database. If anyone can play around with this to figure out why some screen readers struggle with live updates inside Shadow DOM because they don't always detect changes inside the encapsulated component.

<html lang="en"> <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Stats Web Component</title> </head> <body>

    <live-stats></live-stats>

    <script>
        class LiveStats extends HTMLElement {
            constructor() {
                super();
                this.attachShadow({ mode: "open" });
                this.shadowRoot.innerHTML = `
                    <div aria-live="polite" aria-atomic="true" id="counter">Updates: 0</div>
                `;
                this.count = 0;
            }

            updateCount() {
                const counter = this.shadowRoot.getElementById("counter");
                
                // Force DOM mutation to trigger screen reader announcement
                counter.textContent = '\u2060'; // Invisible character
                setTimeout(() => {
                    counter.textContent = `Updates: ${++this.count}`;
                }, 50);
            }
        }

        customElements.define("live-stats", LiveStats);

        // Instantiate the component and update every 3 seconds
        const stats = document.querySelector("live-stats");
        setInterval(() => stats.updateCount(), 3000);
    </script>

</body> </html>

I’m building a custom Web Component that displays live sustainability stats for hotels. The component updates CO₂ savings in real-time, and I want screen readers to announce the updates using an ARIA live region.

The problem is that the screen reader only announces the first update, but ignores all future changes unless I manually re-render the component.

Here’s my Web Component:

class LiveStats extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.innerHTML = `
      <div aria-live="polite" id="co2-output">CO₂ Saved: 10kg</div>
    `;
  }
  
  updateCO2(newValue) {
    const output = this.shadowRoot.getElementById("co2-output");
    output.textContent = `CO₂ Saved: ${newValue}kg`;
  }
}

customElements.define("live-stats", LiveStats);

I expected that calling updateCO2(15) would trigger a screen reader announcement for the new value.

I also tried forcing a reflow:

output.textContent = "";
setTimeout(() => {
  output.textContent = `CO₂ Saved: ${newValue}kg`;
}, 10);

But this doesn’t work reliably.

Are ARIA live regions inside Shadow DOM unreliable, or is there another technique to ensure screen readers consistently read dynamic updates?

UPDATE: The following is some dummy code. The page only has a single Web Component (live-stats) with a simple counter that updates every 3 seconds. The text "Updates: 0" is a placeholder—it doesn’t represent real-world data. The page doesn’t connect to a backend, API, or external database. If anyone can play around with this to figure out why some screen readers struggle with live updates inside Shadow DOM because they don't always detect changes inside the encapsulated component.

<html lang="en"> <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Stats Web Component</title> </head> <body>

    <live-stats></live-stats>

    <script>
        class LiveStats extends HTMLElement {
            constructor() {
                super();
                this.attachShadow({ mode: "open" });
                this.shadowRoot.innerHTML = `
                    <div aria-live="polite" aria-atomic="true" id="counter">Updates: 0</div>
                `;
                this.count = 0;
            }

            updateCount() {
                const counter = this.shadowRoot.getElementById("counter");
                
                // Force DOM mutation to trigger screen reader announcement
                counter.textContent = '\u2060'; // Invisible character
                setTimeout(() => {
                    counter.textContent = `Updates: ${++this.count}`;
                }, 50);
            }
        }

        customElements.define("live-stats", LiveStats);

        // Instantiate the component and update every 3 seconds
        const stats = document.querySelector("live-stats");
        setInterval(() => stats.updateCount(), 3000);
    </script>

</body> </html>
Share Improve this question edited Mar 13 at 9:01 Spekboom07 asked Mar 12 at 10:16 Spekboom07Spekboom07 5412 bronze badges 8
  • 1 As far as I know, shadow DOM / web components have a lot of focus and accessibility bugs, so I wouldn't be surprised about this one just being another one. CAn you provide a test page ? Can you tell which browser and which screen reader you are using ? Thank you. – QuentinC Commented Mar 12 at 16:04
  • I tested this on Chrome and Edge with NVDA, and the issue still persists. I might try it on a few different browsers to see if there's any variation. Unfortunately, I can’t share any details about the page since it’s not live yet. I know this probably makes answering more difficult... – Spekboom07 Commented Mar 12 at 16:56
  • I agree with Quentin, shadow DOM still has lots of accessibility issues and should be avoided if possible. – Roland McLeod Commented Mar 12 at 22:56
  • I can’t share any details about the page since it’s not live yet ==> OK, but you can certainly create a simplified, minimal version which reproduces the issue. The content can be completely dummy, a simple counter updated every few seconds can be sufficient. – QuentinC Commented Mar 13 at 5:30
  • Hey @QuentinC, I updated the post and added a minimal version - see above. It works but is very inconsistent with live data. – Spekboom07 Commented Mar 13 at 9:03
 |  Show 3 more comments

1 Answer 1

Reset to default -1

I tried the dummy code with NVDA V2024.4.2.35031 and Chrome V134.0.6998.89 and everything worked, it seems your problem is due to a browser or screen reader issue. As you stated in the comment's the problem was due to the browser being outdated.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论