details标签默认不形成Tab顺序链,需确保每个summary满足可聚焦、在Tab流中、未被隐藏三条件;应保持DOM自然顺序,避免tabindex="0"干扰,用toggle事件+activeElement判断实现焦点归位。

details标签默认不形成Tab顺序链,必须显式干预
多个 <details> 并排写在 HTML 中时,浏览器**不会自动把它们串成 Tab 焦点流**。用户按 Tab 键,焦点可能直接跳过所有 <summary>,落到后面的按钮或输入框上——这不是 bug,是规范行为:<summary> 本身可聚焦,但 DOM 顺序中若被其他元素“隔开”,或存在 tabindex="-1" / hidden / aria-hidden="true" 等干扰项,就会断链。
真正起作用的是:每个 <summary> 是否满足「可聚焦 + 在 Tab 流中 + 未被隐藏」三个条件。缺一不可。
-
<summary>默认可聚焦(等价于tabindex="0"),无需额外加tabindex - 确保它们在 DOM 中连续、无
display: none或hidden的父容器包裹 - 检查是否误加了
tabindex="-1"到<summary>或其父<details>上(这会让它彻底退出 Tab 流) - 避免在
<summary>上设置pointer-events: none—— 虽然不影响 Tab,但会破坏点击展开逻辑,间接导致用户无法手动触发状态,进而误判为“焦点失效”
用 tabindex="0" 强制纳入焦点流反而容易出错
给 <summary> 显式加 tabindex="0" 不仅多余,还可能埋坑:如果多个 <details> 是动态渲染(比如 React/Vue 条件渲染),tabindex="0" 会在节点销毁时让焦点“掉出”当前区域,跳回页面顶部;更糟的是,若某处写了 tabindex="1",整个 Tab 顺序会被打乱,屏幕阅读器用户可能先聚焦页脚再回到标题。
正确做法是信任原生行为,只做最小修复:
立即学习“前端免费学习笔记(深入)”;
- 保持
<summary>在 DOM 中的自然顺序,视觉布局用 CSS(Flex/Grid)调整,不改源码顺序 - 若某组
<details>被包在<div hidden>里,移除hidden或改用inert(现代浏览器支持良好) - 需要临时禁用某个
<summary>?设tabindex="-1"+aria-disabled="true",并用 CSS 灰化样式,而不是删掉或 display:none - 测试时用键盘 Tab 从第一个
<summary>开始,看是否能逐个抵达,中间有没有“跳空”或“卡住”
切换到下一个 details 的 summary 需要 JS 主动接管
原生 <details> 不提供「按 Enter 切到下一个」这种高级导航。如果用户展开一个后想快速切到下一个 <summary>(类似 Tab 标签页的右箭头行为),就得监听 keydown 并手动聚焦。
关键不是遍历所有 <summary>,而是找“下一个可见且可聚焦”的那个:
- 用
document.querySelectorAll('details > summary')获取全部,再过滤:el.offsetParent !== null && !el.hasAttribute('aria-disabled') - 当前聚焦的是第 i 个,则下一个索引是
(i + 1) % validSummaries.length(循环切换) - 调用
validSummaries[nextIndex].focus()前,确保它没被hidden或inert拦截 - 别监听
keydown里的Tab键——那是浏览器原生逻辑,覆盖它会导致可访问性断裂;应监听ArrowRight或自定义快捷键
多 details 共存时的焦点归位最容易被忽略
当用户展开一个 <details>,再点击另一个触发展开时,焦点往往还留在上一个 <summary> 上,造成“操作了但焦点没跟上”的割裂感。这不是样式问题,是焦点管理缺失。
真正的麻烦点在于:你得判断「这次点击是主动切换,还是意外失焦」。稳妥做法是监听 details 的 toggle 事件,并在事件处理器里做两件事:
- 查当前
document.activeElement是否是本次触发的<summary>;如果不是,说明用户点了别的地方,就不要动焦点 - 如果是,且该
<details>正在展开(event.target.open === true),则立即event.target.querySelector('summary').focus() - 注意:不能在
toggle事件里直接focus()所有 summary,否则 Safari 下会触发两次焦点跳转
复杂点在于,如果页面同时存在手风琴(accordion)和独立 details,它们的焦点策略要分开处理——前者要求互斥展开+焦点强制迁移,后者允许并行+焦点保留在触发点。混用时,光靠 CSS 无法区分,必须靠 JS 标记数据属性,比如 data-accordion-group="faq"。



















