方向键滚不动?带你搞懂 tabindex 的工作原理

作者:刘锦泉

一、客户的反馈

小刘是公司的一名前端工程师。某天,他在业务反馈群里看到客户抱怨:

"xx 页面用方向键居然滚不动了!"

客户还补充:

"前两天还好好的,今天突然就不行了。鼠标滚轮正常、拖动滚动条正常,偏偏就是键盘方向键没反应。"

小刘立刻响应,同时心里纳闷:
"奇怪,方向键滚动是浏览器的默认行为啊,怎么会突然坏掉?难道有人动过它?"

为了弄清楚,他决定亲自重现场景。

二、初步排查

接到反馈后,小刘首先排除了兼容性问题。

打开页面一看,果然和客户描述一致:滚轮、拖动条正常,方向键没反应

他皱起眉头:

"这可不是常见的问题啊......"

小刘随即翻看了发布记录,本以为是最近上线的功能导致,结果发现近期并没有相关发布。

"没发版,怎么会突然坏掉呢?"

为了进一步确认,他又在本地拉起项目。结果方向键一切正常。

线上不行,本地正常 ------情况更加诡异了。

他开始怀疑:问题不在业务逻辑,而是和 运行环境 有关。

很快,他联想到团队最近刚上线的 微前端多 Tab 缓存架构

"会不会是多 Tab 把键盘事件或者焦点劫持了?"

带着疑问,他继续深入排查。。

三、锁定元凶

经过一层层调试,小刘把范围缩小到 Antd Tabs 组件

为了验证,他单独写了一个demo(codesandbox.io/p/sandbox/j...) 包裹内容区并设置滚动。

结果问题果然复现:只要页面在 Tabs 内,方向键就会失灵。

小刘眼前一亮:

"果然,罪魁祸首就是 Tabs组件!"

但他没有急着下结论,而是去社区翻阅资料。果不其然,在在 Ant Design GitHub issues (github.com/ant-design/...) 中,他找到了几乎一致的反馈。

原来,Antd Tabs 在渲染时,会给每个 TabPane 设置 tabindex=0 来管理焦点切换。但这带来一个副作用:

当用户点击某些非可聚焦元素时,焦点会"掉到"外层这个 div 上,从而劫持方向键,导致页面无法滚动:

小刘恍然大悟:

"原来是 tabindex 在背后搞鬼啊!之前还真没怎么注意过这个属性。"

于是,他决定彻底弄清楚 tabindex 的原理。。

四、问题原理解析

1.焦点与可聚焦元素

在应用程式型页面中,往往存在许多需要用户交互的元素。焦点(Focus) 是键盘交互的核心机制,它决定了用户当前能操作的对象。

在 HTML 中,元素可分为 可聚焦(focusable) 与 不可聚焦。常见的默认可聚焦元素包括(可聚焦元素详细兼容性(allyjs.io/data-tables...) ):

  • <a> 标签(带 href 属性)
  • <link> 标签(带 href 属性)
  • <button>
  • <input>(排除 type="hidden"
  • <select>
  • <textarea>

这些元素天然支持一下能力:

  • 键盘聚焦(通过 Tab 键切换)
  • 脚本聚焦(通过 element.focus()

👉 可以通过 document.activeElement 查看当前获得焦点的元素:

javascript 复制代码
document.querySelector("a").focus();
console.log(document.activeElement); // 当前聚焦的元素

2.tabindex 是什么

tabindex 是所有 HTML 元素的通用属性,用于控制:

  • 元素是否能被聚焦
  • Tab 键导航的顺序(通常使用Tab键,因此得名)

非聚焦元素设置 tabindex 属性即可拥有可聚焦元素的能力,示例:

typescript 复制代码
 <div tabindex="0"> 非聚焦元素,使用 tabindex 聚焦</div>

效果:

3.tabindex 的使用

tabindex 的最大值不应超过 32767。如果没有指定,它的默认值为 0。

tabindex 接受一个整数作为值,具有不同的结果,具体取决于整数的值。

  • tabindex="0"
    • 元素可聚焦
    • 焦点顺序按照 DOM 的排列顺序
  • tabindex="-1"
    • 元素不可通过 Tab 键导航获得焦点
    • 但仍可通过 JavaScript focus() 主动聚焦
  • tabindex="1" 或更大值
    • 元素可通过 Tab 键获得焦点
    • 焦点顺序由开发者手动指定
    • ❌ 这种做法会打乱默认顺序,是一种 反模式(anti-pattern),应避免使用

👉 在实际开发中,应 仅使用 0 -1

五、解决方案与实践

小刘最终总结出几种方案:

  1. 去掉 Tabs 的 tabindex**
    • 优点:方向键立刻恢复正常
    • 缺点:容器不能再通过 Tab 键聚焦
  2. 为滚动容器设置 tabindex="0"
    • 优点:可通过 Tab 聚焦后,用方向键滚动
    • 缺点:需要所有滚动容器都加 tabindex,代价过大

经过评估,小刘最终选择:去掉 Tabs 的 tabindex

这样不仅缩小了影响面,还能保证子应用页面任何位置都能正常使用方向键滚动。

六、终篇

一次看似离奇的"方向键失灵",最终真凶竟是一个小小的 tabindex

小刘从这次经历中总结出三点经验:

  • 键盘焦点管理的重要性:不仅要考虑鼠标用户,也要考虑键盘用户和无障碍场景。
  • 合理使用 tabindex0 用于需要聚焦的容器,-1 用于仅脚本控制,避免大于 1。
  • 多关注社区:很多"诡异问题",社区里可能早有人遇到。

最终,小刘把整个排查过程和 tabindex 的知识点记录下来,分享给团队和社区:

细节决定体验,一个小小的属性,可能改变整个页面的交互。

相关推荐
1024小神17 小时前
vue/react项目如何跳转到一个已经写好的html页面
vue.js·react.js·html
Maschera9620 小时前
扣子同款半固定输入模板的简单解决方案
前端·react.js
webKity20 小时前
React 的基本概念介绍
javascript·react.js
YuspTLstar1 天前
从工作原理入手理解React一:React核心内容和基本使用
react.js
__M__1 天前
Zalo Mini App 初体验
前端·react.js
程序员小续1 天前
告别重复造轮子!看 ahooks 如何改变你的代码结构
前端·javascript·react.js
大力yy1 天前
从零到一:VS Code 扩展开发全流程简介(含 Webview 与 React 集成)
前端·javascript·react.js
OEC小胖胖1 天前
掌握表单:React中的受控组件与表单处理
前端·javascript·react.js·前端框架·react·web
梨子同志1 天前
Redux
react.js