animationend 事件必须绑定到应用动画的元素本身,不冒泡;需兼容 vendor prefix;通过 e.animationName 区分动画;推荐使用 { once: true } 防止重复绑定。

animationend 事件监听必须绑定到动画元素本身
很多人把 animationend 监听器加在父容器或 document 上,结果根本收不到事件——它只在**实际应用了 CSS 动画的 DOM 元素**上触发,且不冒泡(除非显式设置 bubbles: true)。如果你给 .box 加了 animation: slideIn 0.3s,监听就必须写在那个 .box 元素上。
常见错误写法:document.addEventListener('animationend', handler) → 不会触发parentEl.addEventListener('animationend', handler) → 即使子元素动了也不会触发(默认不冒泡)
正确做法:
const el = document.querySelector('.box');
el.addEventListener('animationend', () => {
console.log('动画结束');
});
注意 vendor prefix 和事件名兼容性
老版本 Safari(≤9)、Android Browser 等需要监听带前缀的事件名,现代浏览器统一支持 animationend,但如果你的项目还要兼容 iOS 9 或更低,得同时监听多个:
-
animationend(标准) -
webkitAnimationEnd(Safari ≤9、旧 Chrome) -
oanimationend(Opera Presto,已基本淘汰)
推荐写法(轻量兼容):
立即学习“前端免费学习笔记(深入)”;
function onAnimEnd(el, callback) {
const events = ['animationend', 'webkitAnimationEnd'];
events.forEach(type => {
el.addEventListener(type, function handler(e) {
if (e.animationName === 'slideIn') { // 可选:过滤特定动画
callback(e);
}
el.removeEventListener(type, handler); // 防止重复触发
});
});
}
animationName 属性帮你区分多个动画
一个元素可能被多次触发不同动画(比如先 fadeIn 再 slideUp),仅靠 animationend 无法知道是哪个结束了。这时必须检查事件对象的 e.animationName 字段:
el.addEventListener('animationend', e => {
if (e.animationName === 'fade-out') {
el.remove();
} else if (e.animationName === 'bounce') {
el.classList.add('done');
}
});
注意:e.animationName 返回的是 CSS 中定义的动画名(如 @keyframes fade-out),不是 class 名,也不是 transition 名。如果动画名含空格或特殊字符,它也会原样返回。
容易踩的坑:
- 动画名大小写敏感(
FadeOut≠fadeout) - 用
animation: none中断动画时,animationend不会触发 - 动画时长设为
0s,事件仍会同步触发(但视觉无变化)
避免重复绑定和内存泄漏
动态创建元素并频繁添加动画时,容易忘记移除监听器,导致同一元素上堆积多个 animationend 回调。尤其在单页应用中反复挂载/卸载组件时很危险。
安全做法有三种:
- 用
once: true选项(推荐,现代浏览器都支持):el.addEventListener('animationend', handler, { once: true }) - 手动
removeEventListener(需保留 handler 引用) - 用事件委托 +
e.target判断(仅适用于可预测的父容器结构)
另外,如果动画被 JavaScript 中断(例如通过 el.style.animation = 'none'),animationend 不会触发——这点和 transitionend 行为一致,得自己补逻辑。
真正麻烦的点在于:CSS 动画没有“取消回调”机制,也没有“是否已完成”的查询 API。你得靠状态变量 + 事件 + 清理意识,才能稳住节奏。



















