JavaScript onanimationend 异常行为的原因分析与修复方案
问题复现代码
JavaScript
const highlight = (element) => { element.classList.remove('highlight'); setTimeout(() => { element.classList.add('highlight'); }, 0); element.onanimationend = () => { element.classList.remove('highlight'); } } document.querySelector('body').addEventListener('click', event => { if (event.target.hash) { event.preventDefault(); const element = document.querySelector(event.target.hash); highlight(element); } });
CSS
@keyframes highlight { from { background: red; } to { background: white; } } .highlight { animation: highlight 5s; }
HTML
<ul> <li> <a href="#foo">Foo</a> <div> <ul> <li> <a href="#foo-1">Foo 1</a> </li> <li> <a href="#foo-2">Foo 2</a> </li> <li> <a href="#foo-3">Foo 3</a> </li> </ul> </div> </li> <li> <a href="#bar">Bar</a> </li> <li> <a href="#baz">Baz</a> </li> </ul> <hr> <div id="foo"> <h2>Foo</h2> <ul> <li id="foo-1">Foo 1</li> <li id="foo-2">Foo 2</li> <li id="foo-3">Foo 3</li> </ul> </div> <div id="bar"> <h2>Bar</h2> </div> <div id="baz"> <h2>Baz</h2> </div>
问题原因
核心问题是CSS动画事件会冒泡,再加上你用了element.onanimationend这种赋值式的事件绑定:
- 点击
Foo 1后,它的动画启动,同时绑定了onanimationend回调; - 3秒后点击
Foo,Foo元素也绑定了自己的onanimationend回调; - 再过2秒,
Foo 1的动画结束,触发它的onanimationend事件,这个事件会冒泡到父元素Foo; - 因为
Foo的回调没有判断事件来源,直接执行了移除highlight类的操作,导致Foo的动画被提前终止。
修复方案
以下两种方式任选其一即可:
方案1:在回调里判断事件目标是否为当前元素
修改highlight函数,只在当前元素自己的动画结束时才移除类:
const highlight = (element) => { element.classList.remove('highlight'); setTimeout(() => { element.classList.add('highlight'); }, 0); element.onanimationend = (event) => { // 仅处理当前元素触发的动画结束事件 if (event.target === element) { element.classList.remove('highlight'); } } }
方案2:用addEventListener绑定并清理旧监听
这种方式更严谨,避免残留监听或重复绑定的问题:
const highlight = (element) => { // 先移除之前的监听,避免冲突 element.removeEventListener('animationend', handleAnimationEnd); element.classList.remove('highlight'); setTimeout(() => { element.classList.add('highlight'); }, 0); function handleAnimationEnd(event) { if (event.target === element) { element.classList.remove('highlight'); // 动画结束后移除监听,避免内存泄漏 element.removeEventListener('animationend', handleAnimationEnd); } } element.addEventListener('animationend', handleAnimationEnd); }
内容的提问来源于stack exchange,提问作者Three Year Old
相关产品推荐
相关产品推荐

