html全局属性需精准使用:tabindex="0"仅用于非原生可聚焦元素并配role、keydown监听和:focus-visible;禁用tabindex="1";title移动端失效,应改用aria-describedby+真实dom;contenteditable必加tabindex="0"且设spellcheck="false"。

HTML全局属性不是“加了就有效”的装饰项,用错一个就可能让键盘用户卡在页脚、屏幕阅读器跳过关键操作、移动端提示彻底消失。
tabindex="0"该加在哪,不加会怎样
只加在非原生可聚焦但需键盘交互的元素上,比如 <div role="button"> 或 <code><h3 role="tab"></h3>。原生 <button></button>、<a href></a>、<input> 本身就在 Tab 流中,加 tabindex="0" 反而可能干扰 React 的焦点管理或 SSR hydration。
- 不加
tabindex="0":自定义按钮容器无法被 Tab 键到达,键盘用户直接跳过整个交互区 - 加错位置:给纯图标
<span class="icon-close"></span>加tabindex="0",结果焦点停住却无响应,屏幕阅读器读作“无名 span” - 必须同步配
role属性,否则辅助技术无法识别语义;必须监听keydown处理Enter和Space;必须提供:focus-visible样式,否则键盘用户看不见焦点
为什么永远不能写 tabindex="1"
tabindex="1" 不会让元素变成“第一个被 Tab 到的”,只会把它和所有其他正整数一起塞进 Tab 流最前面,再按数值升序排——而原生可聚焦元素(<button></button>、<a></a>)会被挤到最后,造成断层。
- 多个
tabindex="1"元素,浏览器只认 DOM 中第一个,其余无效 - React 动态插入新组件后,硬编码的
1无法重算,今天焦点从左到右,明天从上到下 - iOS Safari 直接无视
tabindex="1",控制台可能报警告 - 屏幕阅读器把第一个
tabindex="1"当作逻辑起点,哪怕它是页脚的“导出”按钮,主编辑区上下文全丢
title 属性在移动端和无障碍场景下的真实表现
title 的悬停延迟由浏览器硬编码决定(Chrome/Firefox/Edge 为 500–1000ms),无法通过 CSS 或 data-delay 修改;它不生成 DOM 节点,CSS 选不到、动画控不住;移动端基本失效——iOS Safari 和主流安卓 WebView 不响应 hover,长按唤起的是系统菜单,不是提示框。
- 常见误操作:给
title元素套transition,毫无反应 - 伪元素模拟
:hover::after只能控制显示延迟,隐藏仍是瞬时,且移动端完全不可用 - 真正可用的平替是
aria-describedby+ 真实 DOM 提示元素:<button aria-describedby="tip-1">删除</button>+<div id="tip-1" role="tooltip" hidden>删除后不可恢复</div> - 注意:
role="tooltip"必须配aria-describedby,否则读屏软件跳过;tooltip 元素要挂载在document.body下,避免被overflow: hidden裁切;卸载时必须手动remove(),否则内存泄漏
contenteditable 元素缺 tabindex="0" 就等于没加
contenteditable 不是“加了就能编辑”的开关。缺 tabindex="0",键盘根本进不去,WCAG 2.1 2.1.1 直接不通过。它只认三个合法值:"true"、"false"、空字符串(等价于 true),其他如 "on" 或 "plaintext-only" 全部被忽略。
- 光标点不进去、Tab 跳不过去 → 缺
tabindex="0" - 能聚焦但中文/符号无法输入 → 子元素带
user-select: none或pointer-events: none - 拼写检查下划线干扰 UI → 忘加
spellcheck="false" - 正确写法:
<div contenteditable="true" tabindex="0" spellcheck="false">编辑区</div>
最常被忽略的点是:提示文案若承载关键操作后果(如“将清空所有缓存”)、法律声明或步骤指引,就不能只靠 title 或 JS tooltip 呈现——必须同步提供显式文本,放在按钮旁、折叠面板内或表单校验旁。否则,仅依赖视觉悬停或焦点触发的提示,在无鼠标、无触控、无语音反馈的场景里,信息就彻底丢失了。











