当 aria-expanded 为真时,如何让屏幕 reader 正确地说出 'button expanded'

How do I get a screen reader to correctly say 'button expanded' when aria-expanded is true

我正在努力使我们的手风琴可以通过 aria-expanded 等咏叹调标签访问。当单击手风琴触发器标题或按下键盘上的 'return' 按钮时,aria-expanded 的值会正确更改。出于某种原因,尽管我用来测试的 ChromeVox 最初只会说 'button collapsed',但一旦点击后值发生变化,就不会说 'button expanded'。

我查看了 https://www.w3.org/TR/wai-aria-practices/examples/accordion/accordion.html 等其他网站上的示例,ChromeVox 在那里正确读取了 aria-expanded 的两种状态,所以我认为这与我的代码结构有关阻止 ChromeVox 宣布 'expanded' 状态。

这是一个手风琴部分的示例:

<div class="accordion-section">

    <button tabIndex="0" id="rules_list" class="accordion" aria-expanded="false" aria-controls="sectRules">
        <span class="title">Rules</span>
    </button>

    <div class="content" role="region" id="sectRules" aria-labledby="rules_list">

        <h4>What rules must all visitors follow?</h4>

        <ul class="list">
            <li style="list-style-type:disc; border-top:0px; margin-bottom:0px; padding-bottom:0px; padding-top:10px; overflow:visible;">rule 1</li>
            <li style="list-style-type:disc; border-top:0px; margin-bottom:0px; padding-bottom:0px; padding-top:10px; overflow:visible;">rule 2</li>
            <li style="list-style-type:disc; border-top:0px; margin-bottom:0px; padding-bottom:0px; padding-top:10px; overflow:visible;">rule 3 etc..</li>
        </ul>

    </div>

</div>

相关的js是:

/* Generic Accordion */
$('.accordion .title').click(function() {
    $(this).parent().parent().children('.content').toggle();
    $(this).toggleClass('open');

    $(this).hasClass("open") ? $(this).parent().attr("aria-expanded", "true") : $(this).parent().attr("aria-expanded", "false");
});

/* Adding support for keyboard */
$('.accordion').on('keydown', function(e) {
    if (/^(13|32)$/.test(e.which)) {
        e.preventDefault();
        $(this).find('.title').click();
    }
});

相关的CSS是:

.accordion + .content {
    display: none;
    margin-top: 10px;
    padding: 10px;
}

.accordion + .content.open {
    display: block;
    background-color: white;
}

我很茫然,任何帮助将不胜感激。

您将点击处理程序放在 span 上,而不是按钮上?这似乎不对,可能是问题的原因,因为 aria-expanded 放在 按钮上

画外音可能在事件目标上寻找 aria-expanded,即 span,而不是按钮。当然它没有找到它。

这或许可以解释为什么它会在按钮获得焦点时宣布状态,而不是在您单击它时宣布状态。

因此请检查是否将点击添加到按钮而不是跨度可以为您提供所需的结果。如果这样做,您可以跳过 toggle().

中的 parent() 步骤之一

此外,我会在 .content 上设置 aria-expanded 并使其与按钮上的 aria-expanded 属性保持同步。

我支持 brennanyoung 所说的关于您使用 span 元素的方式以及为什么这可能是您的代码无法按预期工作的原因。

在我看来,您确实应该考虑使用 button 元素来切换内容,因为这会避免一些额外的工作,例如:

  • 确保 span 覆盖整个按钮,以防止点击按钮时没有任何结果(大声说出来听起来很奇怪),
  • 处理聚焦 span,
  • 确保 spanbutton 一样正常工作(单击、按下和其他 button 相关事件)。

另外,在您的代码中以编程方式触发 click 事件暗示可以做一些更简单的事情。

hidden + aria-labelledby

我倾向于通过在要切换的内容上使用 hidden attribute 和在切换它的按钮上使用 aria-expanded 来使其尽可能简单。

Heydon Pickering 的

Collapsible Sections of the "Inclusive components book" 是一本很好的读物,如果你需要……好吧,可折叠的部分。其实整本书都很棒,没看过的就别浪费时间了。

hidden 属性由屏幕阅读器正确处理,并且会在视觉上和辅助功能树中隐藏元素。您几乎可以在任何最新的网络浏览器 (https://caniuse.com/#search=hidden) 上使用它,这使它成为避免与 类 和 CSS display 属性.

如果您想在内容上使用 aria-labelledby(顺便说一下,有 2 "L",您的代码中缺少一个)(您应该这样做,因为您将其声明为地区),使用 button 文本作为标签效果很好。

但是,如果您打算使用描述操作的文本(例如 "Show the rules" 或 "Hide the rules",具体取决于州),那么这就不再相关了,您将不得不使用另一个元素作为这个地标的标签。您代码中的 h4 元素似乎可以完成这项工作,给它一个 id 将使屏幕阅读器更容易识别区域。

例子

我冒昧地重写了您提供的示例,仅使用纯 JS,并进行了我提到的小调整。可测试here.

<div class="accordion-section" id="accordion-1">

    <button id="rules-list" class="rules-toggle" aria-expanded="true" aria-controls="sect-rules">
        <span>Rules</span>
    </button>

    <section role="region" class="content" id="sect-rules" aria-labelledby="rules-list-title">

        <h4 id="rules-list-title">What rules must all visitors follow?</h4>

        <ul class="list">
            <li>rule 1</li>
            <li>rule 2</li>
            <li>rule 3</li>
        </ul>

    </section>

</div>
const myButton = document.querySelector('#accordion-1 .rules-toggle');
myButton.addEventListener('click', toggleRules);

function toggleRules(evt) {
  const button = evt.currentTarget;
  const accordionSection = button.parentElement;
  const content = accordionSection.querySelector('.content');

  if ('hidden' in content.attributes) {
    content.removeAttribute('hidden');
    button.setAttribute('aria-expanded', true);
  } else {
    content.setAttribute('hidden', true);
    button.setAttribute('aria-expanded', false);
  }
}
.rules-toggle + .content {
  margin-top: 10px;
  padding: 10px;
  background-color: white;
}

.list li {
  list-style-type: disc;
  border-top: 0;
  margin-bottom: 0;
  padding-bottom: 0;
  padding-top: 10px;
  overflow: visible;
}

这是我的最终解决方案,适用于多个部分:

````
   let elementsArray = document.querySelectorAll(".rules-toggle");

   elementsArray.forEach(function(elem) {
      elem.addEventListener("click", function (evt) {
        const button = evt.currentTarget;
        const accordionSection = button.parentElement;
        const content = accordionSection.querySelector('.content');

       if ('hidden' in content.attributes) {
         content.removeAttribute('hidden');
         button.setAttribute('aria-expanded', true);
       } else {
        content.setAttribute('hidden', true);
        button.setAttribute('aria-expanded', false);
       }
     });
});