键盘可访问性依赖DOM顺序、语义结构与JS状态同步三者咬合;禁用table做导航,改用nav+ul;tabindex="0"用于非原生可聚焦元素,"-1"用于动态聚焦,避免正整数;下拉菜单需同步DOM位置、aria-expanded及预设tabindex="-1"并立即focus。

键盘操作不是加几个 tabindex 就能跑通的——它依赖 DOM 顺序、语义结构、JS 状态同步三者咬合,缺一不可。
为什么用 做导航菜单一定会破坏键盘流表格的语义是「展示结构化数据」,不是「提供导航动作」。浏览器和屏幕阅读器会跳过或误读整个区域,Tab 顺序按行/列源码顺序走,而非视觉逻辑;aria-label 在 内基本失效;用户按 Tab 可能直接从页头跳到页脚,中间整块菜单被跳过。
- 改用
结构,哪怕视觉上用 display: grid 或 flex 排版
- 每个菜单项必须是
或带 role="link"/role="button" 的可聚焦元素
- 禁用
的唯一正当理由:你正在渲染真实财务报表,且该表格里没有一个链接
tabindex 的三种取值在导航中该怎么用
tabindex="0" 是默认推荐值,让非原生可聚焦元素(如 )进入标准 Tab 流;tabindex="-1" 是动态聚焦入口,不能靠它“隐藏”元素;tabindex="1" 及正整数会强行打乱全局顺序,应避免。
- 只给有交互意图、又非原生控件的容器加
tabindex="0",比如折叠菜单的触发按钮
-
tabindex="-1" 必须写在目标元素 HTML 源码中(不能 JS 动态插入),否则 .focus() 失效
- 子菜单
展开后,首个子项应保持 tabindex="-1" 并立即 .focus(),不能等用户再按一次 Tab
- 所有
必须显式写 role="navigation",旧版读屏器不认纯
下拉菜单展开后键盘焦点为何卡死?关键在 DOM 和属性同步
视觉展开 ≠ 键盘可达。焦点仍停在父项,方向键进不去子菜单,是因为 DOM 没紧跟、aria-expanded 没更新、子菜单没预设 tabindex="-1"。
- 子菜单
必须紧跟对应 后面,确保 Tab 自然向下流动
- 父项必须同时维护两个属性:
aria-haspopup="menu"(静态)+ aria-expanded="true/false"(动态)
- 展开时调
.focus() 前,先确认子菜单已渲染完成,可用 requestAnimationFrame(() => el.focus())
- 若子菜单被
overflow: hidden 或 transform 遮挡,必须补 el.scrollIntoView({ block: 'nearest' })
移动端侧边栏切换时键盘用户容易掉链子的地方
小屏下点击汉堡按钮展开侧边栏,鼠标用户看到菜单就完事了;键盘用户却可能发现:按 Tab 直接跳过整个区域,或焦点还卡在汉堡按钮上。
Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转
实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。
下载
- 侧边栏容器本身不加
tabindex,首个可操作子项(如第一个 )才加 tabindex="-1"
- 按钮点击后,JS 必须立刻更新
aria-expanded,再延迟一帧执行 .focus()
- 别用
display: none 隐藏侧边栏——它会让所有子元素彻底退出 Tab 流;改用 visibility: hidden + height: 0 + overflow: hidden
- 如果侧边栏是
,确保 aria-label 值明确,不与主导航混淆
真正决定键盘是否好用的,从来不是写了多少个 tabindex,而是每次状态变化后,DOM 是否还在语义路径上、焦点是否被 JS 精准接住、CSS 是否悄悄把可聚焦元素藏起来了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










