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

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


















