NVDA 只读取最后加载的 div 的内容并跳过剩余的内容

NVDA reading only last loaded the content of div and skipping remaining

我有 3 个不同的 div 基于搜索条件动态加载。每个 div 我都添加了 "role='alert'"、"tabindex='0'" 和 "aria-live='polite'"。我注意到 DOM 立即加载了所有 3 个 div 但 NVDA 扬声器只读最后加载的 div 内容。我已经在 NVDA speak viewer 中验证了此处显示的所有 3 div 内容,但在阅读它时说的是最后一个内容。

单击按钮后。 NVDA逐行说出所有内容。

我正在使用Angular 5. 请找到下面的代码以获取更多信息。

HTML 模板:

<input type="button" value="Click Me" tabindex="0" (click)="getWatsonDetails()"/>
<div *ngIf="isHiddenData">
  <div id="main{{i}}" tabindex="-1" class="chat" *ngFor="let passage of watsonResponse;let i=index;">
    <p role="alert" aria-live="polite" aria-atomic="true" tabindex="0" style="font-size: medium; margin-bottom: 4px;">
      {{passage.value.input.text}}
    </p>
    <div role="alert" aria-live="polite" aria-atomic="true" tabindex="-1" style="margin-bottom:4px;" *ngIf="passage.who==='watson'&& passage.label==='DL codes'">
      <span class="btn-group-star" *ngFor="let item of passage.value.codesWithLinks; let pwe=index">
        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="pdfbtn{{i}}{{pwe}}" *ngIf="item.type==='pdf'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}}">
          <span>{{'Document - '+item.code}}</span>
        </button>

        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="wblbtn{{i}}{{pwe}}" *ngIf="item.type==='weblink'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}} ">
          <span role="link">{{'Web Link - '+item.code}}</span>
        </button>

        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="excelbtn{{i}}{{pwe}}" *ngIf="item.type==='excel'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}} ">
          <span>{{'Document - '+item.code}}</span>
        </button>
        <br>
      </span>
    </div>
    <div tabindex="-1" style="font-size: medium; margin-bottom: 4px;" *ngIf="passage.who==='watson' && passage.label==='Relevance node'">
      <p role="alert" aria-live="polite" aria-atomic="true" tabindex="0"> {{passage.value.input.text}} </p> <br />
      <div role="alert" aria-live="polite" aria-atomic="true" style="display:flex;margin-top: 1em;">
        <div id="drop_{{i}}">
          <button role="checkbox" tabindex="0" id="feedbackDropBtn_{{i}}" aria-expanded="false" aria-label="select" class="drop-toggle btn flat">
            <span aria-hidden="true">Select</span>
            <span id="feedbackDropBtn" aria-hidden="true">Select</span>
            <i class="fa fa-angle-down"></i>
          </button>
          <div id="feedback_{{i}}" class="drop-show" *ngIf="!passage.isDisabled" style="height: 85px;margin-left: 3%;overflow-y: scroll;">
            <label id="check{{cb}}" *ngFor="let option of optionsArray;let cb=index">
              <input type="checkbox" id="checkItem_{{i}}{{cb}}" attr.aria-labelledby="{{'chkbx' + cb}}" name="checkbox" tabindex="0" />
              <span id="chkbx{{cb}}">{{option.code}}</span>
            </label>
          </div>
        </div>
        <span aria-live="polite" aria-atomic="true">
          <button type="button" tabindex="0" aria-label="Submit" style="text-align: center;height: 2.2em;margin-left: 0.5em;" class="submitbtn" id="submit_{{i}}">
            <span style="color:#4E8416">Submit</span>
          </button>
        </span>
      </div>
    </div>
  </div>
</div>

TypeScript 代码:

watsonResponse: any = [{ "label": "text", "value": { "input": { "text": "Here are the documents or links that I found that match most closely to your search criteria. Click on a link to view the document in the Document Viewer. If the link is for a webpage, the link will be opened in a new tab in your browser." } }, "who": "watson", "time": "2021-9-3|9:39:29" }, { "label": "DL codes", "value": { "input": { "text": "AB0003,AB0037" }, "codesWithLinks": [{ "code": "AB0003", "link": "", "summary": "Cultural Competency Training 2021", "type": "pdf", "DLWL_links_int": "", "checked": false }, { "code": "AB0037", "link": "", "summary": "Cultural Competency Training 2020", "type": "pdf", "DLWL_links_int": "", "checked": false }, { "code": "None", "checked": false }] }, "who": "watson", "time": "2021-9-3|9:39:29" }, { "label": "Relevance node", "value": { "input": { "text": "Were you able to find what you were looking for?  Please help us continue to improve your experience by selecting the document in the drop-down menu that best helped you and clicking submit for a thumbs-up.  If you did not find what you were looking for, please select “None” from the drop-down and click submit for a thumbs-down to continue." } }, "who": "watson", "time": "2021-9-3|9:39:29" }]

  optionsArray: any = [{ "code": "Dot net" }, { "code": "Java" }, { "code": "Python" }];
  isHiddenData: boolean = false;

  getWatsonDetails() {
    this.isHiddenData = true;
  }

注意:在实时数据通过服务渲染时也忽略CSS。 Click here to see, NVDA speak viewer displaying

这很可能是因为您在这些 div 上使用了 role=“alert”。使用该角色等同于使用 aria-live=“assertive”,这可能会覆盖为屏幕排队等待的消息 reader。

根据@slugolicious 的评论,aria-live (w3.org/TR/wai-aria/#aria-live) 的定义说“用户代理或辅助技术可以选择清除排队的更改当发生果断的变化时。”也就是说,多个 role="alert" 或 aria-live="assertive" 元素可能会清除来自其他元素的消息,因此这可能是问题的原因。

根据评论,在每个警报的 <p> 标签和 <div> 标签上设置 aria-livearia-atomic 可能会导致某些内容一旦初始排队问题得到解决,就会被解析并读取两次。正如 https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions#additional_live_region_attributes 中所述:“aria-atomic=BOOLEAN 用于设置屏幕 reader 是否应始终将实时区域显示为一个整体。”围绕每个警报的内容设置这些属性两次可能会导致某些内容排队等待屏幕读取 reader 两次。

我建议将 <p><div> 标记包装在一个外部元素中,该元素的 aria-live="polite"aria-atomic="true" 仅在每个警报的全部内容周围声明一次.

类似于:

<div id="alertWrapper1" aria-live="polite" aria-atomic="true"
    <p tabindex="-1" style="font-size: medium; margin-bottom: 4px;">
      {{passage.value.input.text}}
    </p>
    <div tabindex="-1" style="margin-bottom:4px;" *ngIf="passage.who==='watson'&& passage.label==='DL codes'">
      <span class="btn-group-star" *ngFor="let item of passage.value.codesWithLinks; let pwe=index">
        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="pdfbtn{{i}}{{pwe}}" *ngIf="item.type==='pdf'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}}">
          <span>{{'Document - '+item.code}}</span>
        </button>

        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="wblbtn{{i}}{{pwe}}" *ngIf="item.type==='weblink'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}} ">
          <span role="link">{{'Web Link - '+item.code}}</span>
        </button>

        <button class="btn btn-secondary wlDlBtn" tabindex="0" id="excelbtn{{i}}{{pwe}}" *ngIf="item.type==='excel'" type="button"
                placement="top-left" attr.aria-label="{{'Document '+item.code}} ">
          <span>{{'Document - '+item.code}}</span>
        </button>
        <br>
      </span>
    </div>
</div>

此外,我还注意到了其他事情:您在原生 <p> 标签上设置了 tabindex="0"。 Per https://www.a11yproject.com/posts/2021-01-28-how-to-use-the-tabindex-attribute/:“有一个神话,将 tabindex="0" 应用于 non-interactive 元素(段落、标题、列表等)有助于通过为使用的人提供一种方式来“改善”可访问性一个屏幕 reader 以专注于网页或网络应用程序中存在的所有内容。不幸的是,这个 well-intentioned 想法实际上并没有创造良好的辅助技术体验。“

作为最佳做法,我建议您避免在 non-interactive 内容上使用 tabindex="0"。我体验过的每个屏幕 reader 都允许用户扫描并访问 non-interactive 网页上的文本内容,而无需使用 tabindex 属性。