【问题标题】:Make table row or cell be read by a table as clickable使表格行或单元格被表格读取为可点击
【发布时间】:2020-11-10 20:15:46
【问题描述】:

您好,我正在处理一个表格,我想提高它的可访问性,但问题是它是使用具有“onClick”属性的表格行创建的,并且在使用屏幕阅读器读取单元格和表格时,例如VoiceOver、NVDA 或 JAWS,它不会将该行读取为可点击或任何会告诉用户点击该行会将他带到另一个页面的标志。

表是由JQuery Datatables动态创建的,表的sn-p是这样的:

<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>

我目前创建的解决方案是添加一个&lt;a&gt;&lt;/a&gt; 环绕每个&lt;td&gt;,因此使屏幕阅读器说“链接”,但我看到其他表格说“可点击”而不需要链接,但我不知道'通过查看 HTML 不知道他们是如何做到的,他们似乎只是带有 onClick on tr 元素的常规表格。

这里的一个不好的迹象可能是“角色”属性破坏了我所读到的语义,该表不是由我创建的,因此我不知道是否会出现这种情况。

【问题讨论】:

    标签: html datatable accessibility screen-readers


    【解决方案1】:

    前言

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

    首先将下面的选项 1 和 2 替换为 &lt;button&gt;s 为 &lt;a&gt;。不需要事件侦听器或 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>
    

    然后,屏幕阅读器将显示“Blah Blah,Blah Blah,Blah Blah,单击以在新页面中打开”。然后,它会填充屏幕阅读器通过无法使用锚点(我们丢失的)读出的信息

    简答

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

    一个项目必须是可聚焦的,才能有点击通知。

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

    但是有更好的选择,详情如下。

    长答案

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

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

    在预期行为和可访问性与实施时间方面,以下是从最佳到最差的几个选项(第二个选项是最佳选项,但可能需要付出很多努力才能实施)。

    1。好 - 改为向列添加按钮 - 易于实现

    在行的开头(或结尾,如果更合适的话)添加一列,并将&lt;button&gt; 添加到该列中以执行操作。

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

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

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

    您可以在服务器上视觉隐藏的&lt;span&gt; 中构建字符串,或通过 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")。

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

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

    W3C example

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

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

    tabindex="0" 添加到&lt;tr&gt;

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

    对于使用屏幕阅读器的鼠标用户,仍然不会有点击通知,但这就是 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>

    以上是最简单的解决方法,但从可访问性的角度来看并不理想,因为引入了很多制表位。它仍然可以在只有几行的表上使用。


    什么不能做

    不要在锚&lt;a&gt; 中换行

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

    请勿在 &lt;tbody&gt; 上使用 role="alert"

    如果您试图提醒屏幕阅读器注意表格中的更改,请询问另一个问题,详细说明您要宣布的更改,我会帮助您。

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

    不要将&lt;buttons&gt; 添加到每个&lt;td&gt; 或使用tabindex="0" 使每个&lt;td&gt;(单元格)可点击

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

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

    其他一些意见/建议

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

    【讨论】:

    • 这是一个非常彻底的答案,感谢格雷厄姆。是的,我看到这个表格以奇怪的方式具有奇怪的属性,即使表格不是可编辑的,但它的属性可能被视为可编辑的意图。这张桌子不是我自己做的,所以我必须先完成它正在建造的东西。谢谢。
    • 是的,我讨厌继承这样的东西,我感受到你的痛苦!有时将其撕开并重新开始会更容易。如果你这样做了,那就选择role="grid" 模式,因为那是最好的。也为 &lt;button&gt; 的错误道歉 - 我已经添加了一个前言,希望能解决这个问题,但任何问题都可以问。
    【解决方案2】:

    尝试使&lt;tr&gt; 元素可点击可能会出现问题,我不建议这样做。

    另外,我不建议用&lt;a&gt; 包装&lt;td&gt; 元素,因为它不是有效的HTML。因此,辅助技术可能会以不可预测的方式运行。

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

    将原生 HTML 语义与按钮元素结合使用

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

    <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>
    

    在表格单元格上使用 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" 在桌面上建立了一个ARIA live region,它将通过辅助技术宣布对内容的任何更改。这不应该破坏任何语义,但它可能是也可能不是你想要的。

    【讨论】:

    • 这真的很有帮助,也是的,我没有在可点击的表格上找到任何东西,这让我觉得正如你所说,这不是一个标准,甚至不是一个好的做法,表格是制作的不考虑可访问性。但我会尝试你的一些建议,谢谢。
    猜你喜欢
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-14
    相关资源
    最近更新 更多