使用 mat-date-range-input 但 JAWS 阅读器首先使用 JAWS-e 读取结束日期(下一个编辑框)

发布于 2025-01-11 12:11:56 字数 4822 浏览 0 评论 0原文

我有一个带有 mat-date-range-input 的组件。 我的客户使用 JAWS 阅读器并使用 JAWS-e(下一个编辑框)进行导航。 当他们执行此操作时,JAWS 首先读取 mat-date-range-input 的“结束日期”。再次按下 JAWS-e 时,将读取“开始日期”。我原以为“开始日期”会先被读到。

下面是带有 mat-date-range-input 的 html 部分。 我首先感觉 aria-hidden=true 的跨度可能是问题的原因,但删除它并没有解决问题。

以前有人经历过吗?有解决办法吗?

谢谢和问候, 内勒克

<div _ngcontent-ynn-c151="" class="ng-star-inserted" style="">
   <mat-form-field _ngcontent-ynn-c151="" appearance="fill" class="mat-form-field date-picker ng-tns-c64-23 mat-primary mat-form-field-type-mat-date-range-input mat-form-field-appearance-fill mat-form-field-can-float mat-form-field-has-label mat-form-field-hide-placeholder ng-star-inserted">
      <div class="mat-form-field-wrapper ng-tns-c64-23">
         <div class="mat-form-field-flex ng-tns-c64-23">
            <div class="mat-form-field-infix ng-tns-c64-23">
               <mat-date-range-input _ngcontent-ynn-c151="" role="group" class="mat-date-range-input ng-tns-c64-23" aria-labelledby="mat-form-field-label-37" data-mat-calendar="mat-datepicker-2">
                  <div cdkmonitorsubtreefocus="" class="mat-date-range-input-container">
                     <div class="mat-date-range-input-start-wrapper">
                        <input _ngcontent-ynn-c151="" type="text" matstartdate="" name="startDate" class="mat-start-date mat-date-range-input-inner startDate ng-touched ng-pristine ng-valid" placeholder="startdatum" id="mat-date-range-input-0" aria-haspopup="dialog" min="1800-01-01T00:00:00+00:19" max="2022-03-02T00:00:00+01:00">
                        <span aria-hidden="true" class="mat-date-range-input-mirror">startdatum</span>
                     </div>
                     <span class="mat-date-range-input-separator mat-date-range-input-separator-hidden">–</span>
                     <div class="mat-date-range-input-end-wrapper">
                        <input _ngcontent-ynn-c151="" type="text" matenddate="" name="endDate" class="mat-end-date mat-date-range-input-inner endDate ng-touched ng-pristine ng-valid" placeholder="einddatum" aria-haspopup="dialog" min="1800-01-01T00:00:00+00:19" max="2022-03-02T00:00:00+01:00">
                     </div>
                  </div>
               </mat-date-range-input>
               <mat-date-range-picker _ngcontent-ynn-c151="" class="ng-tns-c64-23"></mat-date-range-picker>
               <span class="mat-form-field-label-wrapper ng-tns-c64-23">
                  <label class="mat-form-field-label ng-tns-c64-23 mat-empty mat-form-field-empty ng-star-inserted" id="mat-form-field-label-37" for="mat-date-range-input-0" aria-owns="mat-date-range-input-0">
                     <mat-label _ngcontent-ynn-c151="" class="ng-tns-c64-23 ng-star-inserted">Datum (max 28 dagen)</mat-label>
                  </label>
               </span>
            </div>
            <div class="mat-form-field-suffix ng-tns-c64-23 ng-star-inserted">
               <mat-datepicker-toggle _ngcontent-ynn-c151="" matsuffix="" class="mat-datepicker-toggle ng-tns-c64-23" data-mat-calendar="mat-datepicker-2">
                  <button mat-icon-button="" type="button" class="mat-focus-indicator mat-icon-button mat-button-base" aria-haspopup="dialog" aria-label="Open calendar" tabindex="0">
                     <span class="mat-button-wrapper">
                        <mat-icon _ngcontent-ynn-c151="" role="img" matdatepickertoggleicon="" class="mat-icon notranslate material-icons mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">date_range</mat-icon>
                     </span>
                     <span matripple="" class="mat-ripple mat-button-ripple mat-button-ripple-round"></span>
                     <span class="mat-button-focus-overlay"></span>
                  </button>
               </mat-datepicker-toggle>
            </div>
         </div>
         <div class="mat-form-field-underline ng-tns-c64-23 ng-star-inserted">
            <span class="mat-form-field-ripple ng-tns-c64-23">
            </span>
         </div>
         <div class="mat-form-field-subscript-wrapper ng-tns-c64-23">
            <div class="mat-form-field-hint-wrapper ng-tns-c64-23 ng-trigger ng-trigger-transitionMessages ng-star-inserted" style="opacity: 1; transform: translateY(0%);">
               <div class="mat-form-field-hint-spacer ng-tns-c64-23"></div>
            </div>
         </div>
      </div>
   </mat-form-field>
</div>

I have a component with a mat-date-range-input.
My client uses the JAWS reader and uses JAWS-e (next edit box) to navigate.
When they do this JAWS first reads the 'end date' of the mat-date-range-input. The 'start date' is read when JAWS-e is pressed again. I had expected that the 'start date' would be read first.

Below is the part of the html with the mat-date-range-input.
I first had the feeling that the span with aria-hidden=true could be the cause of the issue but removing it didn't fix it.

Has anyone experienced this before? Is there a solution?

Thanks&Regards,
Nelleke

<div _ngcontent-ynn-c151="" class="ng-star-inserted" style="">
   <mat-form-field _ngcontent-ynn-c151="" appearance="fill" class="mat-form-field date-picker ng-tns-c64-23 mat-primary mat-form-field-type-mat-date-range-input mat-form-field-appearance-fill mat-form-field-can-float mat-form-field-has-label mat-form-field-hide-placeholder ng-star-inserted">
      <div class="mat-form-field-wrapper ng-tns-c64-23">
         <div class="mat-form-field-flex ng-tns-c64-23">
            <div class="mat-form-field-infix ng-tns-c64-23">
               <mat-date-range-input _ngcontent-ynn-c151="" role="group" class="mat-date-range-input ng-tns-c64-23" aria-labelledby="mat-form-field-label-37" data-mat-calendar="mat-datepicker-2">
                  <div cdkmonitorsubtreefocus="" class="mat-date-range-input-container">
                     <div class="mat-date-range-input-start-wrapper">
                        <input _ngcontent-ynn-c151="" type="text" matstartdate="" name="startDate" class="mat-start-date mat-date-range-input-inner startDate ng-touched ng-pristine ng-valid" placeholder="startdatum" id="mat-date-range-input-0" aria-haspopup="dialog" min="1800-01-01T00:00:00+00:19" max="2022-03-02T00:00:00+01:00">
                        <span aria-hidden="true" class="mat-date-range-input-mirror">startdatum</span>
                     </div>
                     <span class="mat-date-range-input-separator mat-date-range-input-separator-hidden">–</span>
                     <div class="mat-date-range-input-end-wrapper">
                        <input _ngcontent-ynn-c151="" type="text" matenddate="" name="endDate" class="mat-end-date mat-date-range-input-inner endDate ng-touched ng-pristine ng-valid" placeholder="einddatum" aria-haspopup="dialog" min="1800-01-01T00:00:00+00:19" max="2022-03-02T00:00:00+01:00">
                     </div>
                  </div>
               </mat-date-range-input>
               <mat-date-range-picker _ngcontent-ynn-c151="" class="ng-tns-c64-23"></mat-date-range-picker>
               <span class="mat-form-field-label-wrapper ng-tns-c64-23">
                  <label class="mat-form-field-label ng-tns-c64-23 mat-empty mat-form-field-empty ng-star-inserted" id="mat-form-field-label-37" for="mat-date-range-input-0" aria-owns="mat-date-range-input-0">
                     <mat-label _ngcontent-ynn-c151="" class="ng-tns-c64-23 ng-star-inserted">Datum (max 28 dagen)</mat-label>
                  </label>
               </span>
            </div>
            <div class="mat-form-field-suffix ng-tns-c64-23 ng-star-inserted">
               <mat-datepicker-toggle _ngcontent-ynn-c151="" matsuffix="" class="mat-datepicker-toggle ng-tns-c64-23" data-mat-calendar="mat-datepicker-2">
                  <button mat-icon-button="" type="button" class="mat-focus-indicator mat-icon-button mat-button-base" aria-haspopup="dialog" aria-label="Open calendar" tabindex="0">
                     <span class="mat-button-wrapper">
                        <mat-icon _ngcontent-ynn-c151="" role="img" matdatepickertoggleicon="" class="mat-icon notranslate material-icons mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">date_range</mat-icon>
                     </span>
                     <span matripple="" class="mat-ripple mat-button-ripple mat-button-ripple-round"></span>
                     <span class="mat-button-focus-overlay"></span>
                  </button>
               </mat-datepicker-toggle>
            </div>
         </div>
         <div class="mat-form-field-underline ng-tns-c64-23 ng-star-inserted">
            <span class="mat-form-field-ripple ng-tns-c64-23">
            </span>
         </div>
         <div class="mat-form-field-subscript-wrapper ng-tns-c64-23">
            <div class="mat-form-field-hint-wrapper ng-tns-c64-23 ng-trigger ng-trigger-transitionMessages ng-star-inserted" style="opacity: 1; transform: translateY(0%);">
               <div class="mat-form-field-hint-spacer ng-tns-c64-23"></div>
            </div>
         </div>
      </div>
   </mat-form-field>
</div>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

极致的悲 2025-01-18 12:11:56

无论您使用什么屏幕阅读器,而不仅仅是 JAWS,都会出现此问题。

Angular Material 错误使用了 aria-owns 属性。您可以在他们的“日期范围选择”示例上进行测试。他们的代码基本上是这样的:

<input placeholder="Start date" id="startdate">
<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>

所有 ARIA 属性aria-owns 只是对屏幕阅读器的提示。 ARIA 属性应该帮助屏幕阅读器理解页面的组织。 ARIA 属性不会影响页面的外观,也不会影响浏览器行为。在这种特殊情况下,ARIA 属性不会影响浏览器的 Tab 键顺序。

因此,无论 ARIA 属性是否存在,当您通过日期范围小部件 tab 时,Tab 顺序(默认情况下)与 DOM 顺序相同。在这种情况下, 位于第一个,因此它首先接收选项卡焦点。下一个 选项卡 转到下一个 DOM 元素,即 不是制表位。

这一切都按预期进行。

如果您使用屏幕阅读器命令进行导航,就会变得很奇怪。屏幕阅读器用户可以正常使用 tab 键,他们会按照正确的顺序听到内容。但是,如果屏幕阅读器用户尝试导航 DOM(实际上是 AOM - 可访问性对象模型),无论是通过 E 键(通常是 INS+E< /kbd>) 或 downArrow 键,then aria-owns 会影响事物,因为 ARIA 属性会影响屏幕读者。

另一个需要理解的概念是 AOM。可访问性对象模型类似于文档对象模型 (DOM),只不过它本质上是 DOM 的子集。并非 DOM 上的所有内容都会在 AOM 中。当您使用屏幕阅读器命令时,屏幕阅读器与 AOM 交互。

例如,如果您有一个带有 display:none 的元素,则该元素仍然存在于 DOM 中。您仍然可以对其调用getObjectByName()。但该元素将从 AOM 中删除,以便屏幕阅读器用户无法再使用屏幕阅读器命令导航到该元素。

正如OP中所述,类似的事情也发生在aria-hidden上。将 aria-hidden 设置为 true 将从 AOM 中删除该元素,以便屏幕阅读器不再认为它在那里。

因此,如前所述,downArrow 键(或专门用于输入字段的 E 键)可用于遍历 AOM。它让用户可以像浏览文本文档一样浏览 AOM。可以将其想象为在文本编辑器中读取 HTML 文件,然后使用 downArrow 键转到编辑器中的下一行。

aria-owns 更改 AOM 顺序,而无需物理更改 DOM。 aria-owns 可让您影响 AOM 中的父/子关系,并模仿一个元素位于另一个元素之后,即使它实际上不在 HTML 文件中。

因此,在这种情况下,即使 DOM 物理上看起来像这样:

<input placeholder="Start date" id="startdate">
<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>

AOM 看起来像这样:

<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>
<input placeholder="Start date" id="startdate">

因为 上的 aria-owns="startdate"导致 startdate 元素位于标签之后

是的,这很复杂,但一旦你了解了造成这种情况的原因,就会发现事情很简单。

有解决办法吗?

不:-( 除了联系谷歌让他们知道他们没有正确使用 aria-owns 。

This problem occurs no matter what screen reader you use, not just JAWS.

Angular Material is mis-using the aria-owns attribute. You can test this on their "Date Range Selection" example. Their code is basically like this:

<input placeholder="Start date" id="startdate">
<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>

Like all ARIA attributes, aria-owns is just a hint to the screen reader. ARIA attributes are supposed to help the screen reader understand the organization of the page. ARIA attributes do not affect the appearance of the page nor do they affect how the browser behaves. In this particular case, the ARIA attributes do not affect browser's tabbing order.

So whether ARIA attributes exist or not, when you tab through the date range widget, the tab order (by default) is the same as the DOM order. In this case, the <input placeholder="Start date" id="startdate"> is first so it receives tab focus first. The next tab goes to the next DOM element which is the <input placeholder="End date">. The <label> is not a tab stop.

This all works as expected.

Where it gets funky is if you navigate using screen reader commands. A screen reader user can use the tab key as normal and they will hear things in the right order. But if the screen reader user tries to navigate the DOM (really the AOM - accessibility object model), whether via the E key (usually it's INS+E) or the downArrow key, then aria-owns will affect things, because ARIA attributes affect the screen reader.

One other concept to understand is the AOM. The Accessibility Object Model is like the Document Object Model (DOM) except it's esentially a subset of the DOM. Not everything on the DOM will be in the AOM. A screen reader interacts with the AOM when you use screen reader commands.

For example, if you have an element with display:none, that element still exists in the DOM. You can still call getObjectByName() on it. But the element will be removed from the AOM so that the screen reader user can no longer navigate to that element using screen reader commands.

A similar thing happens with aria-hidden, as noted in the OP. Setting aria-hidden to true will remove that element from the AOM so the screen reader no longer thinks it's there.

So, as mentioned earlier, the downArrow key (or E key specifically for input fields) can be used to walk the AOM. It lets the user walk the AOM as if it were a text document. Think of it like reading the HTML file in a text editor and you use the downArrow key to go to the next line in the editor.

aria-owns changes the AOM order without physically changing the DOM. aria-owns lets you affect the parent/child relationship in the AOM and mimics an element being after another element even if it physically isn't in the HTML file.

So in this case, even though the DOM physically looks like this:

<input placeholder="Start date" id="startdate">
<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>

The AOM will look like this:

<input placeholder="End date">
<label for="startdate" aria-owns="startdate">Enter a date range</label>
<input placeholder="Start date" id="startdate">

because the aria-owns="startdate" on the <label> causes the startdate element to be after the label.

Yes, it's complicated but once you see what's causing it, it's kind of simple.

Is there a solution?

No :-( other than contacting google to let them know they're not using aria-owns correctly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文