Enter The Dragon (Drop):可访问列表重新排序
已发表: 2022-03-10作为一名专注于可访问性的 Web 开发人员,多年来,我主要处理广泛采用的标准化 UI 组件,这些组件得到了辅助技术 (AT) 的良好支持。 对于这些类型的小部件,有简洁的 ARIA 创作实践以及诸如 axe-core 之类的出色工具,可用于测试 Web 组件的可访问性问题。 创建不太常见的小部件,尤其是那些没有广泛采用的用户交互约定的小部件可能非常棘手。
我遇到的最艰巨的挑战之一是可重新排序的拖放列表。 虽然可重新排序的列表是一种比较常用的小部件,对鼠标用户来说具有直观的约定,但尚不清楚仅使用键盘的辅助技术用户如何执行这个简单的任务。 由于缺乏支持的 ARIA 属性,Dragon Drop 使用实时区域来传达所有用户重新排序列表所需的信息。
实时区域
活动区域是配备 ARIA 属性的 HTML 元素,可用于通知屏幕阅读器内容更改。 它们允许我们提供完全定制的屏幕阅读器公告,而无需移动焦点! 实时区域非常适合页面远程部分的实时更新、状态更新和时间敏感信息。
它们通常用于聊天日志、进度指示器、体育比分更新和天气警报,但应谨慎使用,因为它们很容易为屏幕阅读器用户创建过于冗长的体验。 如果您是 live region 的新手,或者只是想探索他们可以做什么,请查看我的 live region playground,它允许您配置自己的自定义 live region。
如果您想在应用程序中使用实时区域,请参阅 npm 上的实时区域模块。
<div aria-live="assertive" role="log" aria-relevant="additions" aria-atomic="true"></div>
为什么使用实时区域?
在理想情况下,我可以简单地依赖拖放式 ARIA 属性aria-grabbed
和aria-dropeffect
。 然而,实际上,对这些属性的支持很少见,而且在支持的情况下,对于屏幕阅读器用户来说,体验是令人困惑和违反直觉的。 最重要的是,这两个属性自 ARIA 1.1 以来已被弃用,这意味着我们将来不会看到对这些属性的支持增加。
可以在此处找到有关此弃用的 W3C 对话。 由于这些问题,我决定不在 Dragon Drop 中使用aria-grabbed
和aria-dropeffect
。 在辅助技术/浏览器配对中,对 ARIA 属性的不同支持在可访问性世界中非常普遍。 幸运的是,诸如role
、 aria-live
、 aria-relevant
和aria-atomic
等实时区域属性得到了 JAWS、NVDA 和 VoiceOver 等屏幕阅读器的广泛支持。
优化的可访问性
Dragon Drop 是一个高度可配置的列表重新排序模块,适用于鼠标、键盘和辅助技术用户。 几年前,当我决定编写它时,在我一直从事的项目中多次提出了可访问的重新排序列表,但我还没有看到一个可行的解决方案。 在我遇到的几十个拖放列表重新排序插件中,大多数在设计时都没有考虑到可访问性,因此非常难以访问。
Dragon Drop 已经过 VoiceOver、JAWS 和 NVDA 测试,使 AT 用户能够成功地重新排序列表。
我着手回答任何用户在遇到可重新排序的列表时可能遇到的所有问题。 这些问题已经通过常见的约定为有视力的鼠标用户提供了答案,但其他用户呢?
我如何取货?
通过提供一个传达项目抓取状态的控件以及一些顶级帮助文本,我们可以回答这个问题。 例如,一个带有可访问文本的控件(由 AT 收集,尽管它的名称、角色和值)“重新排序项目 1,切换按钮”告诉用户它是一个按钮,当激活时,它将拾取项目并启动一个重新排序。
Dragon drop 使用aria-pressed
属性让 AT 用户知道一个项目什么时候被“拖动”,什么时候没有。 它可以被配置为允许整个项目是可拖动的,或者只是一个子“拖动手柄”,在任何一种情况下都可以确保role="button"
和tabindex="0"
的存在。
当拖动项目被激活时, aria-pressed="true"
被应用到元素并且一个可配置的公告,例如“项目 1 已被抓取”通过活动区域被读出给屏幕阅读器。
如何移动项目?
利用aria-describedby
将控件与有用的帮助文本相关联,例如“激活重新排序按钮并使用箭头键重新排序列表或使用鼠标拖动/重新排序”。 告诉用户如何进行实际重新排序。 这让屏幕阅读器用户知道,当按下某个项目时,向上和向下箭头键将分别在列表中上下移动该项目。
如何丢弃物品?
因为使用了aria-pressed
属性,所以用户可以很容易地知道如何放置一个项目。 以某种方式、形状或形式,所有最广泛使用的屏幕阅读器都传达了切换按钮的按下状态。 aria-pressed 属性设置为“false”,当一个项目被删除并且自定义公告(如“项目 1 已删除” )被朗读给屏幕阅读器时。
我如何知道列表何时重新排序?
每次使用向上和向下箭头键更改列表的顺序时,我们都需要确保通知所有用户此更改。 对于盲人用户,我们必须再次依赖直播区域。 可配置的公告,例如“列表已重新排序,第 1 项现在在列表中排在第 4 位。” , 被读出以传达重新排序列表的更新状态。 这很重要,因为视力正常的用户可以立即看到更改顺序的视觉反馈,并且需要将相同的信息传达给 AT 用户。
如何取消重新订购?
由于我们不能依赖广泛采用的约定来进行此类交互,因此我们可以简单地在帮助文本中包含诸如“按转义键取消重新排序”之类的指示。 此外,我们利用实时区域提供定制的读数,通知用户取消。
键盘交互
钥匙 | 行为 |
---|---|
输入或空格 | 在“抓取”和“丢弃”状态之间切换项目 |
“↓” | 在列表中向下移动“抓取的”项目 |
“↑” | 在列表中向上移动“抓取”的项目 |
退出 | 取消重新排序并恢复初始订单 |
看龙在行动
查看 Dragon Drop 演示,您可以在其中体验几种不同的配置。
将 Dragon Drop 放入您的应用程序
Dragon Drop 将您的普通列表转换为完全可访问的拖放可重新排序列表:
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> const dragon = document.getElementById('dragon'); // Enter the dragon new DragonDrop(dragon); </script>
安装
Dragon Drop 是一个开源(MIT 许可)项目,可以通过 npm 安装:
$ npm install drag-on-drop
它可以与 browserify 或 webpack 等模块一起使用:
// if you're not down with ES6, you can require('drag-on-drop') import DragonDrop from 'drag-on-drop';
使用 unpkg CDN 也可以轻松地将 Dragon Drop 拖放到您的页面中:
<script source="https://unpkg.com/drag-on-drop"></script> <script> var dragonDrop = new DragonDrop(listElement); </script>
配置
为了支持广泛的用例,Dragon Drop 是非常可配置的。
下面是默认配置:
{ item: 'li', handle: 'button', activeClass: 'dragon-active', inactiveClass: 'dragon-inactive', announcement: { grabbed: el => `Item ${el.innerText} grabbed`, dropped: el => `Item ${el.innerText} dropped`, reorder: (el, items) => { const pos = items.indexOf(el) + 1; const text = el.innerText; return `The list has been reordered, ${text} is now item ${pos} of ${items.length}`; }, cancel: 'Reordering cancelled' } }
公告
Dragon Drop 的“公告”配置选项是最重要的,因为它是 Dragon Drop 提供的屏幕阅读器体验的支柱。 它是一个包含"grabbed"
、 "dropped"
、 "reorder"
和"cancel"
函数的对象,允许为发生的所有交互自定义实时区域公告。
这些函数中的每一个都必须返回一个公告文本字符串,当给定操作发生时,该字符串将添加到活动区域。 利用这些功能的另一个好处是它支持本地化实时区域消息。
为了便于通知,发生动作的列表项元素和列表中的项数组分别作为参数传递。
{ announcement: { // grabbed is called when an item is picked up grabbed: (targetItem, items) => `${targetItem.innerText} grabbed`, // dropped is called when an item is dropped dropped: (targetItem, items) => `${targetItem.innerText} grabbed`, // reorder is called each time the order of the list is altered reorder: (targetItem, items) => { return `${targetItem.innerText} is now ${items.indexOf(targetItem) + 1} of ${items.length}` }, // cancel is called when a reordering is cancelled (via escape key) cancel: () => 'The initial order has been restored, reordering cancelled' } }
帮助文本
提供描述如何使用可重新排序列表的帮助文本是绝对重要的。 这是 Dragon Drop 不会为您做的事情,以便在如何将此文本提供给辅助技术方面保持不那么固执己见。 推荐的实现是使用aria-describedby
将帮助文本与交互式项目相关联,如下所示:
<p>Activate the reorder button and use the arrow keys to reorder the list or use your mouse to drag/reorder. Press escape to cancel the reordering.</p> <ul> <li> <button>Reorder Item 1</button> <span>Item 1</span> </li> <li> <button>Reorder Item 2</button> <span>Item 2</span> </li> <li> <button>Reorder Item 3</button> <span>Item 3</span> </li> </ul>
GitHub上的龙滴
Dragon Drop的第三个版本最近已经发布。 如果您有兴趣使用它,请参阅 GitHub 上的 Dragon Drop 文档。 特别感谢 Dragula 的创建者,Dragon Drop 用于鼠标交互的模块,以及设计了令人敬畏的标志的 Aaron Pearlman!
龙的未来
如果未来在 WAI-ARIA 技术规范中加入拖放交互,Dragon Drop 依赖非标准交互和直播区域的情况可能会发生变化。 我将继续进行测试,确保它得到尽可能多的屏幕阅读器的良好支持,并与最新的 ARIA 规范保持同步。 此外,还有很多功能正在开发中,包括支持触摸屏/移动设备以及多列列表(如冲刺板)。 未来可能添加的另一个功能是 Dragon Drop React 组件。
目前,Dragon Drop 可以与 React 一起使用,如下面的演示所示,但它并不理想,因为 React 不会拾取由列表重新排序引起的 DOM 更改,这可能会导致意外行为。 我敦促任何在 Dragon Drop 中发现错误,甚至对功能有想法的人在 GitHub 上创建问题。 欢迎并非常感谢所有反馈和贡献!