如何实现每次为元素添加类时CSS动画都能触发
现有代码
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div style="position:relative"> <p> HERE IS A TESTER FOR YOU. </p> </div> <input type="button" id="cloc" value="test">
CSS
input { margin-bottom:20px; margin-top:50px; } p { opacity:1; } .anim { animation-duration: 200ms; animation-name: slidein; } @keyframes slidein { from { transform:translateX(-200px); opacity:0; } to { transform:translateX(0px); opacity:1; } } p { position:absolute; left:0px; opacity:0; animation-fill-mode: forwards; animation-iteration-count: 1; }
JavaScript
$( document ).ready(function() { jQuery('#cloc').click(function () { jQuery('p').removeClass('anim').promise().done(function() { jQuery('p').addClass('anim'); }); }); });
问题原因
- 浏览器动画状态记忆:CSS动画在元素上执行一次后,浏览器会记录该动画已完成,即使移除再重新添加动画类,也不会触发新的动画循环。
- 无效的Promise使用:
removeClass是同步DOM操作,promise().done在这里没有实际作用,因为样式变更不会触发jQuery的异步回调。 - CSS样式冲突:
p元素的默认样式里包含animation-fill-mode: forwards等动画属性,导致元素默认状态就绑定了动画相关配置,干扰动画的重新触发。
解决方案
修正后的代码
CSS
input { margin-bottom:20px; margin-top:50px; } p { position:absolute; left:0px; opacity:0; transform: translateX(-200px); /* 初始状态与动画起始帧保持一致 */ } .anim { animation-duration: 200ms; animation-name: slidein; animation-fill-mode: forwards; animation-iteration-count: 1; } @keyframes slidein { from { transform: translateX(-200px); opacity:0; } to { transform: translateX(0px); opacity:1; } }
JavaScript(强制重排方案)
$(document).ready(function() { $('#cloc').click(function () { const $p = $('p'); $p.removeClass('anim'); // 强制触发浏览器重排,让样式变更生效 void $p[0].offsetHeight; $p.addClass('anim'); }); });
JavaScript(setTimeout兼容方案)
$(document).ready(function() { $('#cloc').click(function () { const $p = $('p'); $p.removeClass('anim'); // 延迟0ms让浏览器处理DOM变更后再添加类 setTimeout(() => { $p.addClass('anim'); }, 0); }); });
原理说明
- 把所有动画相关属性移到
anim类中,确保只有添加类时元素才拥有动画配置,默认状态仅保留初始样式。 - 强制重排(
void $p[0].offsetHeight)或setTimeout会让浏览器重新计算元素的样式与布局,此时再添加动画类,浏览器会识别为新的动画触发,从而重新执行动画。
内容的提问来源于stack exchange,提问作者CRAIG
相关产品推荐
相关产品推荐

