open是布尔属性,只检测存在与否;设open="false"仍展开;js应操作open属性而非setattribute;监听状态用toggle事件;无原生禁用方案。

open 属性不是开关,只看“有没有”
open 是布尔属性,浏览器只检测它是否存在,不解析值。写 open="false" 或 open="" 甚至 open="banana",只要属性名出现了,就等价于展开——这是最常踩的坑。
-
<details open></details>→ 展开 -
<details open="false"></details>→ 依然展开(⚠️别这么写) -
<details></details>→ 收起(没写就是没存在)
JavaScript 动态控制必须读写 open 属性,不能用 setAttribute
DOM 元素的 open 属性是反射属性,和实际状态实时同步。用 setAttribute 会把字符串值写进 HTML 属性,反而触发“存在即展开”逻辑。
- ✅ 正确:
el.open = false、console.log(el.open) - ❌ 错误:
el.setAttribute('open', 'false')→ 实际强制展开 - ❌ 错误:
el.hasAttribute('open')≠el.open:初始没写时两者都是false;但执行el.open = true后,hasAttribute仍返回false(HTML 源码没变)
监听展开/收起变化只能用 toggle 事件
<details></details> 没有 change 事件。用户点击 <summary></summary> 切换状态后,触发的是 toggle 事件,此时读 el.open 才是最新值。
- ✅ 正确:
el.addEventListener('toggle', () => console.log(el.open)) - ❌ 错误:
el.addEventListener('change', ...)→ 不会触发 - 注意:事件在状态切换完成后派发,不需要手动判断前后差异
默认展开但想禁用用户交互?没有原生方案
HTML 规范没提供 disabled 或 readonly 属性给 <details></details>。强行禁用得靠 JS 拦截点击,但要注意副作用:
- 拦截
click在<summary></summary>上可能影响键盘焦点和屏幕阅读器行为 - 更稳妥的做法是移除
<summary></summary>的交互语义(比如换成<div>),但会丢失可访问性支持 <li>如果只是视觉上“看起来不可点”,CSS 加 <code>pointer-events: none更轻量,但无法阻止键盘Enter或Space触发











