使 table 行或单元格被 table 读取为可点击

Make table row or cell be read by a table as clickable

你好,我正在开发一个 table,我想提高它的可访问性,但问题是它是用具有“onClick”属性的 table 行创建的,并且在阅读时单元格和 table 带有屏幕 reader,例如 VoiceOver、NVDA 或 JAWS .

table 是由 JQuery 数据 table 动态创建的 table 的片段如下:

<div id="DataTables_Table_1_wrapper" class="dataTables_wrapper" role="grid">
  <table
    class="list lista table table-bordered table-hover dlaList component-editable dataTable"
    data-editable-id="dla28475"
    style="width: 900px"
    id="DataTables_Table_1"
  >
    <thead>
      <tr role="row">
        <th
          class="sorting"
          tabindex="0"
          rowspan="1"
          colspan="1"
          style="width: 230px"
        >
          Activities
        </th>
        <th
          class="sorting_asc"
          tabindex="0"
          rowspan="1"
          colspan="1"
          style="width: 570px"
          aria-sort="ascending"
        >
          Description
        </th>
        <th
          class="sorting"
          tabindex="0"
          rowspan="1"
          colspan="1"
          style="width: 100px"
        >
          Status
        </th>
      </tr>
    </thead>
    <tbody role="alert" aria-live="polite" aria-relevant="all">
      <tr class="odd" onclick="functionForEvent();">
        <td class="">
          <div class="input-sm" status_id="undefined">Blah Blah</div>
        </td>
        <td class="sorting_1">
          <div class="input-sm" status_id="undefined">Blah blah</div>
        </td>
        <td class="">
          <div class="input-sm" status_id="undefined">blah blah</div>
        </td>
      </tr>
    </tbody>
  </table>
</div>

我目前创建的解决方案是在每个 <td> 周围添加一个 <a></a>,因此使屏幕 reader 显示“link”,但我已经看到其他 table 说“可点击”而不需要 link,但我不知道他们是怎么做到的 HTML,它们看起来很正常 tables 在 tr 元素上带有 onClick。

这里的一个不好的迹象可能是“角色”属性破坏了我所读到的语义,table 不是我创建的,所以我不知道情况是否如此。

尝试使 <tr> 元素可点击可能会出现问题,我不推荐这样做。

此外,我不建议用 <a> 包裹 <td> 元素,因为它无效 HTML。因此,辅助技术可能会以不可预测的方式运行。

您可以采取一些可能的方法:

对按钮元素使用原生 HTML 语义

您可能希望设置按钮的样式,使它们具有透明背景、无边框并占据 table 单元格的整个宽度。

<tbody role="alert" aria-live="polite" aria-relevant="all">
    <tr class="odd">
        <td class="">
            <button onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do">
                <div class="input-sm" status_id="undefined">Blah Blah</div>
            </button>
        </td>
        <td class="sorting_1">
            <button onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do">
                <div class="input-sm" status_id="undefined">Blah blah</div>
            </button>
        </td>
        <td class="">
            <button onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do">
                <div class="input-sm" status_id="undefined">blah blah</div>
            </button>
        </td>
    </tr>
</tbody>

在 Table 个单元格上使用 ARIA

<tbody role="alert" aria-live="polite" aria-relevant="all">
    <tr class="odd">
        <td class="" tabindex="0" onclick="functionForEvent();" aria-label="some helpful info on what clicking this will do">
            <div class="input-sm" status_id="undefined">Blah Blah</div>
        </td>
        <td class="sorting_1" tabindex="0" onclick="functionForEvent();" aria-label="some helpful info on what clicking this will do">
            <div class="input-sm" status_id="undefined">Blah blah</div>
        </td>
        <td class="" tabindex="0" onclick="functionForEvent();" aria-label="some helpful info on what clicking this will do">
            <div class="input-sm" status_id="undefined">blah blah</div>
        </td>
    </tr>
</tbody>

在内部 Div 上使用 ARIA

<tbody role="alert" aria-live="polite" aria-relevant="all">
    <tr class="odd">
        <td class="">
            <div tabindex="0" onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do" class="input-sm" status_id="undefined">Blah Blah</div></a>
        </td>
        <td class="sorting_1">
            <div tabindex="0" onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do" class="input-sm" status_id="undefined">Blah blah</div>
        </td>
        <td class="">
            <div tabindex="0" onclick="functionForEvent()" aria-label="some helpful info on what clicking this will do" class="input-sm" status_id="undefined">blah blah</div>
        </td>
    </tr>
</tbody>

仅供参考,tbody 上的 role="alert" 在 table 上建立了一个 ARIA live region,它将通过辅助技术宣布对内容的任何更改。这不应该破坏任何语义,但它可能是也可能不是你想要的。

前言

向 OP 道歉,如果该行旨在将人们带到另一个页面,则此答案中的一些内容可能会产生误导。

首先下面的选项 1 和 2<button> 换成 <a>。不需要事件侦听器或 onclick,因为锚会完成所需的一切。

对于选项 3,我会在文本中添加一些额外的内容来解释这会将您带到一个新页面。

如果您选择使用列文本作为可访问文本(即不要用 aria-label 覆盖),那么您应该添加 aria-describedby 并添加一个段落,上面写着“,点击打开在新页面中。

<p id="newPageText" class="visually-hidden">click to open (new page)</p>

<tr class="odd" tabindex="0" aria-describedby="newPageText">
        <td class="">
          <div class="input-sm" status_id="undefined">Blah Blah</div>
        </td>
        <td class="sorting_1">
          <div class="input-sm" status_id="undefined">Blah blah</div>
        </td>
        <td class="">
          <div class="input-sm" status_id="undefined">blah blah</div>
        </td>
      </tr>

然后屏幕 reader 将显示“Blah Blah,Blah Blah,Blah Blah,单击以在新页面中打开”。然后,由于无法使用锚点

,屏幕 reader 会用语义(我们丢失的)读出信息

简答

您没有听到任何通知的原因是您的行不可聚焦。

项目必须可聚焦才能显示点击通知。

添加 tabindex="0" 将为键盘用户解决此问题,对于鼠标用户,您应该将 cursor: pointer.tr:hover{} 规则添加到您的 CSS.

但是还有更好的选择,详见下文。

长答案

让整行都可以点击并不是最好的主意。

然而,它仍然比我在 “不该做什么” 部分中详述的其他一些选项更可取,因此我将其作为下面的第三个选项包含在内。

根据预期行为和可访问性与实施时间的关系,这里有一些从最好到最差的选项(第二个选项是最佳选项,但可能需要付出很多努力才能实施)。

1。好 - 将按钮添加到列 - 易于实施

在行的开头(或末尾,如果更合适)添加一列,然后将 <button> 添加到该列以执行操作。

删除行上的点击处理程序并将其附加到 <button>(也将其更改为 JavaScript 中的事件侦听器而不是内联 onclick 以使可维护性更容易).

在此按钮中,我们为屏幕 reader 用户/纯文本浏览器用户添加了一些 visually hidden text 以使行关联清晰。

我遗憾地推荐 Visually hidden text as it is still far more robust than aria-label(尽管情况正在好转!)并且还有一个额外的好处,那就是可以一直回到 ie6(尽管如果您的网站在 IE6 中工作,你比我更好!:- ) )!

您可以在服务器上视觉隐藏的 <span> 中构建字符串,或者通过 JavaScript.

构建它

如果有一个特定的列是唯一的/描述性足以知道您正在编辑的内容,则不需要包含所有行信息。我只将所有 3 列的文本添加到示例中,因为我不知道您的数据。

示例 1

.visually-hidden { 
    border: 0;
    padding: 0;
    margin: 0;
    position: absolute !important;
    height: 1px; 
    width: 1px;
    overflow: hidden;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
    clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
    clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
    white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<table>
<tbody>
      <tr class="odd">
       <td>
           <button>
               Perform Action
               <span class="visually-hidden">
                   Column 1, Column 2, Column 3
               </span>
           </button>
       </td>
        <td class="">
          <div class="input-sm" status_id="undefined">Column 1</div>
        </td>
        <td class="sorting_1">
          <div class="input-sm" status_id="undefined">Column 2</div>
        </td>
        <td class="">
          <div class="input-sm" status_id="undefined">Column 3</div>
        </td>
      </tr>
    </tbody>
    </table>

这是跨所有设备的最稳健的方法并且易于实施,这就是为什么它是最推荐的,但是它增加了很多制表位,如果你有时间的话,这不是最好的选择实施网格模式

2。最佳 - 推荐方式(如果你不能添加列) - 难以实施

使用role="grid" and follow the grid pattern.

role="grid" has good screen reader support 并且是语义上最合适的角色。

这里还有很多要求,因为您需要启用键盘控件,例如使用箭头键等。

然后您可以在页面上添加一个视觉上隐藏的段落,上面写着“点击编辑”,并给它一个 id(例如 id="editText")。

然后在每个 <td> 上添加 aria-describedby="editText" 以便 <td> 读取其内容,然后“单击以编辑”。

此时您只需在 JavaScript 中添加一个事件侦听器来侦听点击(而不是使用内联 onclick 属性。)This answer should give you an idea of how to add an event listener to each <td> 如果您不确定。

W3C example

没有我的例子,因为这种方式需要比 Stack Overflow 答案证明的更多的工作!呵呵。但是,如果您可以使它成为可重用的组件,那么它是跨站点重用的好模式。它有一个显着的优势,它只引入一个制表位。

3。平均 - 如果您不能使用 role="grid" 或添加列的可能方法。

tabindex="0" 添加到 <tr>

这将使该行可聚焦,然后当用户通过键盘聚焦该行时宣布“可点击”。

对于使用屏幕的鼠标用户 reader 仍然不会有点击通知,但这就是 cursor: pointer.tr:hover{} 的用途(更改光标并确保在视觉上很明显,当鼠标悬停在一些可见的指示器上时,这是一个可点击的区域。

所有行列在获得焦点时将作为文本一个接一个地读出,这可能不合适您可能想在支持它的浏览器中添加 aria-label 作为覆盖。

如果这样做,请确保您的 tr:focus 指示器样式清晰可见。

tr:hover{
   cursor: pointer;
   background-color: #cccccc;
}
tr:focus{
   border: none;
   outline: 2px solid #333;
   outline-offset: 2px;
}
<table
    class="list lista table table-bordered table-hover dlaList component-editable dataTable"
    data-editable-id="dla28475"
    style="width: 900px"
    id="DataTables_Table_1"
  >

    <tbody>
      <tr class="odd" tabindex="0">
        <td class="">
          <div class="input-sm" status_id="undefined">Blah Blah</div>
        </td>
        <td class="sorting_1">
          <div class="input-sm" status_id="undefined">Blah blah</div>
        </td>
        <td class="">
          <div class="input-sm" status_id="undefined">blah blah</div>
        </td>
      </tr>
    </tbody>
  </table>

以上是最简单的修复,但由于引入了很多制表位,从可访问性的角度来看并不理想。尽管在只有几行的 table 上它仍然可用。


不该做什么

不要在锚点中换行 <a>

无论如何,这在语义上是不正确且无效的 HTML。这会导致某些屏幕出现问题 readers.

请勿在 <tbody> 上使用 role="alert"

如果您正试图提醒屏幕 readers table 中的更改,请问另一个问题,详细说明您要宣布的更改,我会帮助您。

目前它将在页面加载和每次更改时开始读取 table。即使您只是添加一行等,它也会再次读取整个 table,并且可能会在您每次应用过滤器时读取整个 table。正如您可以想象的那样,对于屏幕 reader 用户来说,这将成为一场噩梦!

不要将 <buttons> 添加到每个 <td> 或使用 tabindex="0"

使每个 <td>(单元格)可点击

对于依赖 Tab 键导航的键盘用户(即非屏幕 reader 用户)来说,这将是一场噩梦。

您添加了太多不必要的制表位,页面可能变得无法使用。

其他一些观察/建议

  1. 如果 table 很长并且您最终使用了选项 1 或 3(为每行添加一个制表位),请确保 table 之前有一个 skip link .
  2. 删除前面提到的 role="alert"aria-live="polite"
  3. 对于你的 sortable 列 headers 你最好在 <th> 中添加一个 <button> 触发排序功能,它带有你所有的焦点状态内置等
  4. <div> 中删除 role="grid" - 如果您使用该模式(我给出的选项 2),它应该放在 <table> 上。
  5. 使用事件处理程序而不是 onclick 属性 - 只是性能和可维护性的一般提示 - 与可访问性无关!