原生交互元素(a[href]、button、input、select、textarea、iframe)默认可聚焦;div、span等非交互元素需加tabindex="0"并配套role、keydown监听和焦点样式才真正可用。

绝大多数情况下,给元素加 tabindex="0" 就能让它可聚焦——但仅此一步远远不够,缺了语义、事件和样式,等于开门不装锁,看着能进,实际卡死。
哪些元素默认就可聚焦?
不是所有标签一写上就能按 Tab 进去。原生交互元素自带焦点能力:a(带 href)、button、input、select、textarea、iframe。它们默认等价于 tabindex="0",无需手动添加。
而 div、span、li、p 等非交互语义元素,默认完全不可聚焦——哪怕绑了 click 事件,键盘用户也根本碰不到。
常见错误现象:
- 一个
div上写了“点击展开”,加了click监听,却没处理键盘操作,屏幕阅读器用户卡在那儿动不了 - 用
display: none隐藏了某个button,以为“看不见=不能聚焦”,结果它仍从 Tab 流中消失——但这是因为它被移出了可访问树,不是 tabindex 没生效
为什么 tabindex="0" 后还是没反应?
加了 tabindex="0" 只解决“能不能获得焦点”,不解决“获得焦点后怎么用”。原生按钮按空格/回车会触发,div 不会——它只是个容器,没内置行为。
必须同步做三件事:
- 加
role:比如role="button"或role="checkbox",告诉辅助技术“这是什么” - 监听
keydown:判断event.key === 'Enter'或' '(注意空格字符),并调用对应逻辑;对空格要e.preventDefault(),否则页面会滚动 - 补焦点样式:浏览器默认
outline常被 CSS 清掉,得用:focus-visible或:focus提供可见反馈,否则用户根本不知道焦点在哪
示例(可聚焦且可操作的自定义按钮):
<div tabindex="0" role="button" aria-label="刷新列表">↻ 刷新</div>
JS 配套:
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
const el = document.querySelector('[role="button"]');<br>el.addEventListener('keydown', e => {<br> if (e.key === 'Enter' || e.key === ' ') {<br> e.preventDefault();<br> el.click(); // 或执行你自己的刷新逻辑<br> }<br>});
tabindex="-1" 是禁用焦点?
不是。它是“可编程聚焦,但不进 Tab 流”——最常被误用成“隐藏开关”。
典型场景:
- 模态框打开后,立刻
closeBtn.focus(),前提是它有tabindex="-1" - 下拉菜单项、Tooltip 触发按钮,需要键盘操作(如方向键导航),但不该出现在主 Tab 序列里
- Accordion 展开后,把焦点移到新内容首项,而不是留在触发按钮上
常见踩坑:
- 设了
tabindex="-1",再按 Tab 测试发现跳过了,就以为“失效”,其实设计如此 - 忘了绑定
keydown,用户 Tab 进不去,但用鼠标点进去后按空格也没响应——因为没监听 - 和
aria-hidden="true"同时用:视觉可见但屏幕阅读器忽略,等于“能聚焦但不知道是啥”,体验比不可聚焦还差
绝对别碰 tabindex="1" 或更大的正数
它不是“优先聚焦”,而是“破坏焦点流”的快捷键。
浏览器会先把所有正数按值升序排一遍(tabindex="2" 在 tabindex="10" 前),再把 tabindex="0" 和原生元素按 DOM 顺序排在后面。一旦第三方组件或动态插入内容也设了正数,整个 Tab 顺序就失控。
更麻烦的是移动端 Safari:未被用户主动点击过的页面,tabindex 对非原生元素基本无效——哪怕写了 tabindex="0",首次 Tab 也跳不过去。这时候正数毫无意义,反而让问题更难排查。
真正难的不是填对那个数字,而是理解:焦点流本质是线性路径,任何插队都意味着断裂。DOM 顺序 + tabindex="0" 已足够健壮,其余都是在给自己挖坑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










