原生实现 Tabs 切换需三步:用 button 控制激活态、hidden 属性切换面板、绑定 click 事件并手动 focus;必须同步 aria-selected 与 tabindex,确保键盘和读屏器可用。

怎么用原生 HTML + CSS + JS 实现基础 tabs 切换
不需要框架,纯原生就能做,核心就三件事:控制按钮激活态、隐藏非当前面板、绑定点击事件。关键不是“怎么炫”,而是“怎么稳”——尤其注意 tabindex 和 aria-selected 的同步,否则键盘用户或读屏器会卡住。
实操建议:
立即学习“前端免费学习笔记(深入)”;
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
- 用
<button></button>而非<div> 做 tab 标签,天然支持空格/回车触发,无需额外加 <code>tabindex="0" - 每个 tab 按钮加
role="tab",对应面板加role="tabpanel",并用aria-controls指向面板id - 切换时只改
aria-selected和hidden属性,别直接删 DOM 或用display: none—— 后者会让读屏器跳过内容 - 示例结构:
<div role="tablist"> <button role="tab" aria-selected="true" aria-controls="panel-1">首页</button> <button role="tab" aria-selected="false" aria-controls="panel-2">关于</button> </div> <div id="panel-1" role="tabpanel" tabindex="0">…</div> <div id="panel-2" role="tabpanel" hidden tabindex="0">…</div>
- 每次点击 tab 按钮后,立即对目标按钮调用
.focus(),确保键盘用户能继续操作 - 如果面板内容可聚焦(比如里面有输入框),切换后应把焦点移到面板首个可聚焦元素,而非留在按钮上
- 别依赖
autofocus:它只在页面加载时生效,切换时不触发 - 错误写法:
button.addEventListener('click', () => { showPanel(id); });→ 缺少button.focus() - 必须用
<a href="#tab-1"></a>链接代替按钮,对应面板加id="tab-1" - 用
:target显示面板:section:target { display: block; },其余默认display: none - 无法响应式更新按钮的
aria-selected,读屏器会认为所有 tab 都未选中 - 不支持禁用 tab、加载中状态、动画过渡等交互细节
- 移动端 Safari 对
:target有渲染延迟,可能闪一下才显示 - Vue 中避免用
v-show控制面板显隐——它靠display: none,会破坏 ARIA 可访问性;改用v-if或动态hidden属性 - React 中不要在
useEffect里批量设aria-selected,要确保 DOM 已挂载且属性已渲染,否则读屏器读不到变化 - 组件 props 必须接收
defaultActiveKey和activeKey,支持受控与非受控模式,否则父组件无法干预 tab 状态 - 别在组件内部硬编码
tabindex,应由使用者决定是否允许键盘导航(比如嵌在表单里时可能需要禁用)
为什么 onclick 切换后键盘焦点没跟着走
常见现象:鼠标点第二个 tab,内容变了,但按 Tab 键还是停在第一个按钮上——焦点没迁移。这不是 bug,是没手动管理 focus()。
实操建议:
立即学习“前端免费学习笔记(深入)”;
用 CSS :target 实现无 JS 的 tabs 可行吗
可行,但限制极多,仅适合静态内容、不需状态保持、且 URL 锚点可暴露的场景。一旦刷新页面,当前 tab 就靠 URL hash 决定,用户可能误分享带 hash 的链接。
实操建议:
立即学习“前端免费学习笔记(深入)”;
Vue/React 里封装 tabs 组件要注意什么
不是把 class 切换逻辑搬进去就完事。框架的响应式更新和 DOM 生命周期容易掩盖焦点、ARIA 属性不同步的问题。
实操建议:
立即学习“前端免费学习笔记(深入)”;

















