应使用data-tab显式关联按钮与面板,避免依赖DOM索引;首次加载须手动激活默认项;需支持键盘导航与ARIA属性以保障无障碍。

用 data-tab 关联按钮和面板,别靠 DOM 顺序硬匹配
很多人写标签页时用 querySelectorAll('.tab-item')[i] 和 querySelectorAll('.tab-pane')[i] 按索引配对,结果加个新标签或删一个就全乱。真实项目里结构常变,索引极易错位。
必须用属性显式绑定:按钮带 data-tab="profile",对应面板用 id="profile" 或 data-tab-panel="profile"。这样增删项不影响逻辑,也方便 SSR 渲染后 JS 接管。
display: none vs visibility: hidden:选哪个看内容类型
轻量静态内容(文本、图片)直接用 display: none 最干净,重排开销小;但含 <iframe>、<video> 或需保留输入状态的表单时,display: none 会中断资源加载、清空焦点、丢掉用户已填内容。
这时该用 visibility: hidden + position: absolute 组合隐藏,同时保持 DOM 活跃。记得同步更新 aria-hidden="true",否则屏幕阅读器仍会读取不可见内容。
首次加载必须手动激活一个面板,否则页面空白
这是新手最常漏的一步。JS 监听点击后才切换,但页面刚打开时没有任何 .active 类,所有面板都隐藏——用户看到的就是一片空白。
解决方法:
• HTML 中直接给默认项加 class="active"(如第一个 <li> 和对应 <div>)
• 或在 JS 初始化时主动触发一次切换:document.querySelector('[data-tab-trigger]').click()
• 别依赖 CSS 的 :first-child 自动激活,它不保证语义正确,也不兼容 SSR 场景。
键盘导航和无障碍支持不是可选项,是必做项
只支持鼠标点选的标签页,在无障碍测试里基本等于不可用。用户按 Tab 键进不到标签,方向键切不了页签,Enter 也无法触发——这违反 WCAG 2.1 标准。
必须补全:
• 外层容器加 role="tablist"
• 每个按钮加 role="tab"、tabindex="0",当前激活项设 tabindex="-1"
• 面板加 role="tabpanel" 和 aria-labelledby 指向对应按钮 ID
• 用 keydown 监听 ArrowLeft/ArrowRight 实现左右切换
• 点击或回车后,自动将焦点移到面板首元素(如 panel.firstElementChild?.focus())
立即学习“前端免费学习笔记(深入)”;
实际开发中,最难的不是让标签切换动起来,而是让「切换」这件事在各种边界条件下依然稳定:服务端渲染后 JS 接管不闪屏、键盘操作不卡死、动态插入新 tab 不崩、多实例共存不冲突。这些细节堆起来,才是生产级标签页的真正门槛。



















