details标签必须以summary为首个直接子元素才能交互,open是布尔属性,JS控制需用el.open=true/false,监听状态变化应使用toggle事件而非click。

details 标签本身不展开,必须配 summary 才能点击交互;没写 open 就是默认收起,写了就默认展开——这不是开关逻辑,而是“属性存在即生效”的布尔语义。
为什么点不动?检查 summary 是否为首个直接子元素
浏览器只认严格结构:details 开始后**立刻**跟 summary,中间不能有空格、换行、注释或任何其他元素。哪怕一个 <!-- --> 或 <p></p> 插在前面,summary 就彻底失效,点击无反应,也不报错。
- ✅ 正确:
<details><summary>标题</summary><p>内容</p></details> - ❌ 失效:
<details><p>说明</p><summary>标题</summary></details> - ❌ 失效:
<details> <!-- 注释 --> <summary>标题</summary></details>(注意前后空格)
服务端模板或 JSX 渲染时建议压缩 HTML 输出,或手动写成单行避免隐形字符干扰。
open 属性不是字符串开关,JS 控制必须用布尔赋值
open 是 HTML 布尔属性,只看是否存在,不认值。写 open="false" 或 open="" 都等同于 open,页面加载时仍会强制展开。
立即学习“前端免费学习笔记(深入)”;
- 默认关闭 → 直接写
<details><summary>...</summary></details> - 默认展开 → 写
<details open><summary>...</summary></details> - JS 动态控制 → 用
el.open = true或el.open = false,不要用el.setAttribute('open', 'true') - 读状态别用
el.getAttribute('open'),它返回字符串或null;应直接读el.open(布尔值)
自定义箭头要用 list-style: none + ::before,别碰 ::marker
原生三角由浏览器 UA 样式生成,::marker 在 Safari 旧版基本无效,Chrome 不支持颜色修改,Firefox 虽完整但跨浏览器一致性差。更稳的方案是清掉默认样式,自己注入图标。
- 先重置:
summary { list-style: none; } - 再加图标:
summary::before { content: "▶"; margin-right: 6px; } - 展开时切换:
details[open] summary::before { content: "▼"; } - 避免用
text-indent或负margin遮盖原生箭头——会压缩可点击区域,尤其 iOS Safari 热区本就窄
如果 summary 里嵌了 <h5> 这类块级元素,记得给它加 display: inline-block 和 margin: 0,否则会换行挤歪图标。
监听状态变化必须用 toggle 事件,不是 click
toggle 是唯一能捕获所有触发方式的事件:鼠标点击、键盘空格/回车、以及 JS 修改 el.open。它在 open 属性真实变更后触发,且兼容 Chrome 12+、Firefox 49+、Safari 15.4+。
- ✅ 正确:
el.addEventListener('toggle', () => console.log(el.open)) - ❌ 错误:
summary.addEventListener('click', ...)—— 会漏掉键盘操作,且干扰原生焦点逻辑 -
event.preventDefault()对toggle无效,别白费力气 - 页面首次加载时不会触发
toggle,只响应后续状态变更
最常被忽略的是:DOM 结构错误不报错但功能静默消失,open 被当普通属性用导致 JS 控制失灵,以及把 ::marker 当通用接口强行覆盖——这三处出问题,整个折叠交互就卡死在“看起来像但点不了”。



















