无障碍体验

模态框焦点陷阱:打开、嵌套、关闭和异步消失都要安全

用原生 dialog 或可靠焦点管理处理初始焦点、Tab 循环、背景 inert、Escape、嵌套确认和触发器消失后的恢复。

好的实现不会依赖用户反复刷新碰运气,而会让每个阶段有明确输入、输出、超时和终态。 模态打开时视觉背景不可操作,键盘和辅助技术也必须隔离。初始焦点通常放标题、首个安全输入或取消,而非危险确认。

无障碍不是发布前补一组 aria 属性,而是让键盘、屏幕阅读器、缩放、减弱动态和高对比度用户完成同一任务闭环,并在错误后找得到下一步。

先定义系统契约

实现之前先列出不可违反的不变量,再为性能与体验留出调节参数。参数可以灰度,身份、权限和终态规则不能在运行时随意漂移。

  • 保存 opener 元素但关闭时先检查仍 connected/visible;嵌套模态用栈管理 inert 和焦点,最外层关闭才恢复页面。
  • 把协议事实、用户意图和自动恢复分层;自动化只能恢复事实,不能推翻用户明确选择。
  • 资源释放是协议的一部分:定时器、句柄、队列和临时数据都要在终态中可重复清理。

“模态框焦点陷阱:打开、嵌套、关闭和异步消失都要安全”的交付标准是正常路径可用、异常路径收敛、资源有界,并且用户能理解系统为何采用当前状态。 这样得到的不是演示代码,而是能解释、能降级、能回滚的生产能力。

哪些情况会破坏契约

不要把异常路径理解成弹一次提示。它还包括在途工作如何停、对端如何获知、残留数据如何处置,以及下一次操作是否会继承旧状态。

  • 只监听 keydown Tab 会漏掉脚本聚焦背景;触发任务被实时删除后 focus() 旧节点会失败并留在 body。
  • 失败被压成一个布尔值,调用方无法区分可重试、需用户介入与永久拒绝,最终形成无限循环。
  • 降级路径从未压测,主路径故障时所有流量同时进入更慢、更昂贵的后备方案。

如何逐条验证契约

验证需要先写预期状态轨迹,再执行故障注入。每个阶段同时核对用户可见结果、两端协议状态、持久记录和资源计数,才能证明闭环成立。

  1. 覆盖正反 Tab、Escape、点击遮罩、嵌套取消、异步删除 opener 和路由切换,焦点始终位于当前可操作上下文。
  2. 连续执行一百次开始、失败、重试和取消,检查句柄、监听器、队列及临时数据回到基线。
  3. 分别覆盖直连、中转、弱网、后台标签页和移动端,报告不能只给平均值或一次成功截图。

当实现能够安全降级、重复执行不产生额外副作用、故障指标早于投诉出现时,这项能力才算具备长期维护价值。

把方法变成一次真实连接

打开 uCopy,让两台设备在浏览器里安全连接并开始传输。

开始免费使用