details元素无原生toggle事件,应监听其click事件并比对open属性变化来判断展开/收起状态,避免使用change、transitionend或MutationObserver。

details元素没有原生的toggle事件
HTML <details> 标签在展开/收起时**不会触发 toggle 事件**(尽管规范曾草案定义过,但主流浏览器长期未实现)。你监听 click 或 change 都无法可靠捕获状态切换——click 在任意子元素上都可能触发,change 根本不适用(它只用于表单控件)。
用transitionend + getComputedStyle判断实际展开状态
最稳定的方式是监听 transitionend(前提是 <details> 的 <summary> 后内容有 CSS 过渡),再通过 getComputedStyle 检查 height 或 max-height 变化。但更通用、无需 CSS 配合的做法是:监听 click,然后立即检查 open 属性变化。
- 给
<details>绑定click事件(不是<summary>),因为点击 summary 会触发 details 的 click,且此时open已更新 - 用闭包或 data 属性缓存上一次的
open值,对比当前值判断是展开还是收起 - 避免监听
mousedown或pointerdown,它们发生在open更新前
const details = document.querySelector('details');
let isOpen = details.open;
<p>details.addEventListener('click', () => {
if (details.open !== isOpen) {
isOpen = details.open;
if (isOpen) {
console.log('已展开');
} else {
console.log('已收起');
}
}
});
注意shadow DOM和嵌套summary的干扰
如果 <details> 内部有自定义 <summary>(比如用了 Web Component),或者你在 shadow DOM 中操作,click 事件可能被拦截或冒泡异常。此时应:
- 确保事件监听器绑定在
<details>自身,而非委托到父级 - 避免在
<summary>上调用event.stopPropagation() - 若用 Shadow DOM,需在
open属性变更后手动 dispatch 自定义事件,因为原生行为不会穿透边界
不要依赖MutationObserver监听open属性
虽然 open 是布尔属性,但浏览器**不会在用户交互时触发 attributeChangedCallback 或 MutationObserver** —— 它是内部状态同步,不走 DOM attribute 更新流程。尝试监听 attributes 的 MutationObserver 会失败,且带来不必要的性能开销。
立即学习“Java免费学习笔记(深入)”;
真正需要响应式更新的场景(如 Vue/React 组件封装),应在事件回调中显式调用 setState 或触发自定义事件,而不是幻想 DOM 层自动通知。



















