如何阻止transitionend事件多次触发?已尝试one()等方法无效
这个问题我之前也碰到过!过渡事件触发多次通常有两个原因:一是浏览器对过渡事件的前缀实现(比如webkitTransitionEnd和transitionend会同时触发),二是元素有多个过渡属性(比如同时过渡宽、高、背景色,每个属性结束都会触发一次事件)。再加上你需要区分「添加类的过渡」和「移除类的过渡」,得结合标记位和事件去重来解决。
给你一套可行的方案,一步步来:
1. 统一过渡事件监听,处理兼容性
首先封装一个兼容所有浏览器的过渡结束事件监听方法,同时避免同一过渡的重复触发:
function onTransitionEnd($element, callback) { let triggered = false; const endEvents = ['transitionend', 'webkitTransitionEnd', 'oTransitionEnd', 'MSTransitionEnd']; function handleEnd(event) { // 确保只执行一次回调,避免多属性/多前缀重复触发 if (!triggered) { triggered = true; // 移除所有事件监听 endEvents.forEach(evt => $element.off(evt, handleEnd)); callback(event); } } endEvents.forEach(evt => $element.on(evt, handleEnd)); }
2. 结合临时标记区分「添加类的过渡」
在点击添加类时,给元素加一个自定义属性作为标记,只有当这个标记存在时,我们才处理过渡结束事件;执行完逻辑后移除标记,这样后续移除类的过渡就不会触发回调了:
$(document).ready(function() { $(document).on("click", ".main", function() { const $this = $(this); // 添加类之前先标记这是我们要监听的过渡 $this.attr('data-waiting-transition', 'true'); $this.addClass('example'); // 监听过渡结束 onTransitionEnd($this, function() { // 这里写你需要执行的逻辑,比如: console.log('过渡结束啦!只触发一次'); // 移除标记,后续移除类的过渡不会触发 $this.removeAttr('data-waiting-transition'); }); }); // 如果你有移除类的逻辑,比如另一个点击事件 $(document).on("click", ".remove-class", function() { $('.main').removeClass('example'); // 这里不需要监听过渡结束,因为标记已经被移除了 }); });
为什么之前的方法没用?
one():如果是直接绑定到元素上,它确实只会触发一次,但如果你的元素有多个过渡属性或者浏览器触发了多个前缀事件,one()会针对每个事件各触发一次,所以还是会多次执行。我们上面的封装里用了triggered标志位,确保不管多少个事件触发,回调只执行一次。stopPropagation()/return false:过渡事件是元素自身的事件,不会冒泡到父元素(或者说冒泡不影响多次触发的问题),所以这两个方法对多触发的问题没用。
额外优化:如果需要监听移除类的过渡怎么办?
如果之后你需要区分添加和移除的过渡,可以把标记改成不同的值,比如添加类时设data-transition-type="add",移除时设data-transition-type="remove",然后在回调里判断这个值来执行不同逻辑。
这样就能完美解决你的问题啦!
内容的提问来源于stack exchange,提问作者SilverSurfer
相关产品推荐
相关产品推荐

