details 元素的 toggle 事件不支持 ontoggle 内联属性,必须用 addeventlistener('toggle', callback) 注册;该事件不冒泡,仅响应用户交互(不触发 js 修改 open),需用 e.newstate 判断状态,兼容性差时应降级为 click + settimeout 读 open。

ontoggle 属性根本不能用——它不存在,浏览器不支持内联绑定。
details 的 toggle 事件必须用 addEventListener('toggle', ...)
很多人搜“ontoggle”或写 <details ontoggle="..."></details>,结果回调完全不执行。这不是写法问题,是语法无效:ontoggle 不是标准 HTML 属性,JS 里也没有 details.ontoggle 可赋值。正确方式只有一种:
- 用
addEventListener('toggle', callback)显式注册 - 事件名是字符串
'toggle',不是'ontoggle' - 不能委托到父容器,因为该事件不冒泡
-
e.target永远是<details></details>元素本身,不是<summary></summary>
toggle 事件只响应用户交互,JS 修改 open 不会触发
这是最常踩的坑:你写了 details.open = true 或 details.setAttribute('open', ''),但监听函数没运行。因为 toggle 事件设计上就只对鼠标点击、空格键、回车键等**用户操作**响应。以下全都不触发:
details.open = truedetails.toggleAttribute('open')details.removeAttribute('open')
如果你需要统一响应所有开闭动作(包括 JS 控制),得自己封装一个开关函数,把状态设置和回调调用收拢在一起。
判断展开/收起,优先读 e.newState 而非 e.target.open
ToggleEvent 接口提供 e.newState,值为 "open" 或 "closed",比读 e.target.open 更语义清晰、更可靠:
-
e.newState是只读属性,明确表示切换后的最终状态 - 它在 DOM 更新后同步可用,无需
setTimeout或requestAnimationFrame - 注意:仅
<details></details>支持该属性,<popover></popover>完全没有newState
示例:
details.addEventListener('toggle', (e) => {
if (e.newState === 'open') {
console.log('用户展开了面板');
} else {
console.log('用户收起了面板');
}
});
兼容性差时 fallback 到 click + 延迟读 open
Safari 15.4 之前、旧版安卓 WebView、IE 全系都不支持 toggle 事件。检测方式是:
if ('ontoggle' in document.createElement('details')) {
// ❌ 错误:这个检测永远 false,因为 ontoggle 属性不存在
}
// ✅ 正确检测:
if ('toggle' in Event && typeof details.addEventListener === 'function') {
details.addEventListener('toggle', handler);
} else {
details.addEventListener('click', () => {
setTimeout(() => {
console.log('当前 open 状态:', details.open);
}, 0);
});
}
注意:click 监听要加 setTimeout,因为点击 <summary></summary> 后,open 属性变更不是同步完成的,尤其在 Safari 中。
真正难处理的不是监听本身,而是混用用户点击和 JS 控制后状态与副作用脱节——比如手风琴逻辑里手动关兄弟节点,又同时允许外部按钮直接设 open,很容易漏掉某次状态更新。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











