原生drag/drop事件链不满足wcag 2.1 aa要求,因其缺乏键盘支持、焦点管理、屏幕阅读器语义反馈及视觉-at同步机制;必须改用键盘驱动的“选中-移动-插入”两步操作或严格补全aria描述、aria-live播报和焦点控制。

原生 dragstart/drop 事件链本身不满足 WCAG 2.1 AA 级无障碍要求——它依赖鼠标精确操作、无键盘支持、无焦点管理、无屏幕阅读器语义反馈,直接用于生产环境会卡住视障用户。
为什么 drag/drop 默认不无障碍
浏览器对 draggable="true" 的实现完全绕过焦点流:Tab 键无法抵达、Enter 或 Space 不触发拖拽、屏幕阅读器读不出“可拖拽”状态,更不会播报拖拽位置或目标区域变化。Chrome 和 Firefox 均未为该 API 补充 ARIA 属性映射,role="application" 也不足以兜底。
- 没有
aria-dropeffect或aria-grabbed的标准化支持(已被 WAI-ARIA 1.2 废弃) -
dataTransfer对象不可被 AT(辅助技术)访问,拖拽中数据状态对用户完全黑盒 - 视觉反馈(如
dragover高亮)不自动同步到 AT,需手动用aria-live区域播报
替代方案:用 keyboard + focus 模拟拖拽逻辑
真正可行的无障碍拖拽,是放弃原生 drag API,改用键盘驱动的“移动-插入”两步操作:
- 用
Tab聚焦到可移动项(如表单项容器),按Enter进入“选中态”,此时添加aria-selected="true"和视觉高亮 - 用方向键(
↑/↓或←/→)移动焦点到目标插入位置,实时播报“插入到第 X 项之前” - 按
Shift+Enter确认插入,DOM 移动后立即focus()到新位置,并用aria-live="polite"报读结果 - 全程禁用
draggable="true",避免干扰键盘导航
示例结构:
<div role="list" aria-label="表单字段列表"> <div role="listitem" tabindex="0" aria-label="文本框字段">...</div> <div role="listitem" tabindex="0" aria-label="下拉选择字段">...</div> </div>
若必须用原生 drag,至少补这三处
硬上 drag API 时,最低限度要覆盖以下三点,否则会被无障碍审计工具直接标为严重问题:
- 给每个可拖元素加
aria-roledescription="可拖拽字段"(而非已废弃的aria-grabbed),并在dragstart中动态设aria-describedby指向一个隐藏说明段落 - 目标区域监听
dragenter时,立即更新aria-live="assertive"区域内容,例如:“已进入表单区域,松开鼠标可放置” - 在
drop后,用setTimeout(() => el.focus(), 0)将焦点强制移到新插入项,并确保该项有tabindex="0"
注意:aria-dropeffect="move" 已从 WAI-ARIA 1.2 中移除,任何使用它的代码都属于过时写法。
移动端和屏幕阅读器根本别指望 drag
iOS VoiceOver + Safari、Android TalkBack + Chrome 均不触发 dragstart,连事件监听器都收不到。所谓“触摸长按模拟 drag”是伪需求——AT 会直接接管长按行为(如唤出上下文菜单),不会透传给页面 JS。真实项目里,只要涉及无障碍交付,就必须提供纯键盘路径,且不能把 drag 作为唯一交互方式。
最常被忽略的一点:无障碍拖拽不是加几个 ARIA 属性就能糊弄过去,而是整个交互模型要重设计。键盘用户需要明确的状态提示、可预测的操作节奏、以及失败时的清晰错误反馈——这些原生 drag API 一个都不提供。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











