transitionend 被触发多次的根本原因是每个可过渡css属性独立派发事件;多属性变化、webkit内核对正反向动画分别计数、未校验propertyname、重复绑定监听器及未及时移除,均导致误触发。

transitionend 为什么总被触发多次
根本原因不是浏览器“bug”,而是每个可过渡的 CSS 属性变化都会独立派发一次 transitionend。比如你同时改了 transform 和 opacity,事件至少触发两次;若还动了 color 或 border-color,次数继续叠加。WebKit 内核(Safari、旧版 iOS WebView)更激进:同一属性正向动画结束 + 反向动画结束,各算一次——.box.active 加上又去掉,可能收到四次回调。
只响应目标属性:必须检查 event.propertyName
不加判断就执行逻辑,等于默认接受所有属性变更信号,极易出错。例如 height 动画还没完,opacity 先结束就把元素隐藏了。
- 监听时务必用
if (e.propertyName === 'transform')过滤 - 多个属性协同过渡(如
transform+opacity)时,选一个稳定锚点属性作为完成信号,推荐transform—— 它受伪类或继承干扰少,插值行为更一致 - 避免把
transitionend绑在父容器上监听子元素变化,子元素触发的事件event.target是子元素,但event.propertyName仍需单独校验
防重复绑定和残留监听器
最常见错误是每次交互都调用 addEventListener('transitionend', handler),却忘了清理。快速连点按钮时,监听器越积越多,回调执行次数呈指数增长。
- 在 handler 第一行加
el.removeEventListener('transitionend', handler),确保单次有效 - 如果用 class 切换触发动画,确保
el.classList.add('active')和addEventListener在同一次 JS 执行流里,避免因异步导致监听器注册晚于动画启动 - 别在
hover或频繁切换的 class 中动态绑/解绑,改用状态标记(如el.dataset.isTransitioning = 'true')配合transitionend清理
更稳妥的替代方案:改用 animationend
当业务强依赖“动画真正结束”这个信号时,transitionend 天然不可靠。CSS Animation 的 animationend 只在整段关键帧播放完毕后触发一次,且不受子元素样式干扰。
- 把原来写在
:hover或 class 里的transition拆成@keyframes,例如定义slide-in和slide-out - 用
el.style.animation = 'slide-in 0.3s ease-out'触发,监听animationend即可 - 注意:不要混用
transition和animation控制同一元素的同一属性,否则行为不可预测
实际开发中最易被忽略的是:你以为监听了一次,其实浏览器默默给你发了三次事件,而你没做任何过滤或清理。只要涉及多属性过渡或频繁交互,transitionend 就得配上 event.propertyName 校验和显式移除逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











