transitionend 未触发主因是过渡未真实发生或被js中断;需确保transition属性常态存在、目标属性有显式初始值、避免同步设transition: none后立刻改样式,并手动处理js强制中断后的收尾逻辑。

transitionend 事件不触发,绝大多数情况不是监听写错了,而是过渡压根没发生,或者被 JS 中断后浏览器直接放弃派发。
transitionend 为什么根本没触发
浏览器只在「真实发生插值动画」时才派发 transitionend。常见静默失败场景:
- 目标属性初始值未显式声明(如
opacity没写opacity: 0,而是靠继承或默认值,导致变化量为 0) -
transition只写在:hover或.active类里,元素常态下getComputedStyle(el).transitionProperty === 'none' - JS 中先移除类再监听,或同步设置
el.style.transition = 'none'后立刻改样式,动画流程被截断 - 属性本身不可过渡(如
display、height: auto、z-index),浏览器跳过插值直接赋终值
验证方法:打开 DevTools → Elements → Computed 面板,搜索 transition-property,确认它在「无任何状态类」时就存在且非 none;再查目标属性(如 opacity)当前值是否为明确数字而非 auto 或 initial。
JS 主动中断 transition 后如何补回调
用 el.classList.remove('animating') 或 el.style.transition = 'none' 强制停掉动画时,transitionend 绝对不会来。必须手动收尾:
- 先清除类或设
transition: none - 立即读取一个 layout 触发属性,例如
void el.offsetWidth(强制同步计算当前样式) - 再同步设置终态样式(如
el.style.opacity = '0'),并调用你的完成逻辑 - 若回调里还要改样式,务必先关掉
transition,否则可能触发新过渡,形成套娃
推荐封装成函数:forceTransitionEnd(el, { opacity: 0 }),避免每次重复三步。
监听 transitionend 的兼容与防误触要点
不同浏览器事件名不同,且 transitionend 会冒泡,子元素动画也可能触发父级监听:
- 用
once: true防止多次绑定导致重复执行 - 回调中必须校验
e.target === el和e.propertyName === 'opacity'(或其他你关心的属性),避免子元素或无关属性干扰 - 不要依赖全局事件名,可用检测逻辑获取真实事件名:
const transEndEvent = 'ontransitionend' in document.body ? 'transitionend' : 'webkitTransitionEnd'
监听代码别写成 el.addEventListener('transitionend', handler) 就完事——漏掉校验和清理,90% 的“没触发”其实是触发了但没处理对。
真正卡住人的,从来不是怎么写监听,而是过渡有没有真正启动;而启动的前提,是基础类里有 transition、目标属性有显式初始值、DOM 状态变更之间没被 JS 瞬间覆盖掉上下文。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











