CSS3过渡事件

192

有没有任何事件可以被元素触发来检查CSS3过渡是否已经开始或结束?

6个回答

213

W3C CSS 过渡草案

完成 CSS 过渡后将生成相应的 DOM 事件,每个发生过渡的属性都会触发一个事件。这使得内容开发人员可以执行与过渡完成同步的操作。


Webkit

要确定过渡何时完成,请为发送到过渡结束时的 DOM 事件设置 JavaScript 事件监听器函数。该事件是 WebKitTransitionEvent 的实例,其类型为 webkitTransitionEnd

box.addEventListener( 'webkitTransitionEnd', 
    function( event ) { alert( "Finished transition!" ); }, false );

Mozilla

在过渡完成时,会触发单个事件。在 Firefox 中,该事件是 transitionend,在 Opera 中是 oTransitionEnd,而在 WebKit 中则是 webkitTransitionEnd

Opera

有一种类型的过渡事件可用。当过渡完成时,oTransitionEnd 事件就会触发。

Internet Explorer

在过渡完成时,会触发 transitionend 事件。如果在过渡完成前移除了过渡,则不会触发此事件。


Stack Overflow: 如何跨浏览器标准化 CSS3 过渡效果函数?


3
请注意,在Firefox中,该事件称为“transitionend”,而在Opera中称为“oTransitionEnd”。 - Andreas Köberle
8
没有人提到问题中关于转换开始部分的内容。有没有办法在转换开始之前注册一个事件处理程序? - tyler
现在有一种标准的方法来实现这个吗?似乎两年时间很长!事情可能已经改变了。 - Mild Fuzz
@tyler 我不知道如何解决缺少转换开始的问题。 - Davor Lucic
@Mild Fuzz提供的stackoverflow问题有一个有趣的解决方案。 - Davor Lucic
看起来IE支持transitionend https://github.com/twitter/bootstrap/pull/4109 - Chris Love

78

更新

现代浏览器都支持无前缀事件:

element.addEventListener('transitionend', callback, false);

https://caniuse.com/#feat=css-transitions


我之前一直在使用 Pete 给出的方法,但现在我开始使用以下方法:

$(".myClass").one('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', 
function() {
 //do something
});

如果您使用Bootstrap,则可以简单地执行以下操作:

$(".myClass").one($.support.transition.end,
function() {
 //do something
});

这是因为在bootstrap.js中包含了以下内容

+function ($) {
  'use strict';

  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
  // ============================================================

  function transitionEnd() {
    var el = document.createElement('bootstrap')

    var transEndEventNames = {
      'WebkitTransition' : 'webkitTransitionEnd',
      'MozTransition'    : 'transitionend',
      'OTransition'      : 'oTransitionEnd otransitionend',
      'transition'       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return { end: transEndEventNames[name] }
      }
    }

    return false // explicit for ie8 (  ._.)
  }


  $(function () {
    $.support.transition = transitionEnd()
  })

}(jQuery);

请注意,它们还包括一个emulateTransitionEnd函数,这可能是必要的,以确保始终发生回调。

  // http://blog.alexmaccaw.com/css-transitions
  $.fn.emulateTransitionEnd = function (duration) {
    var called = false, $el = this
    $(this).one($.support.transition.end, function () { called = true })
    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
    setTimeout(callback, duration)
    return this
  }

请注意,有时此事件不会触发,通常是因为属性未更改或未触发绘制。为了确保我们始终获得回调,请设置一个超时,手动触发该事件。

http://blog.alexmaccaw.com/css-transitions


3
你不能做那样的事情。在某些情况下,回调函数可能会被触发多次。 - sebastian
11
对于那些仍保留前缀和常规事件名称的浏览器,您可以通过使用 ".one" 而不是 ".on" 来解决这个问题。 - AlexG
只是提醒一下:第一个解决方案看起来很好用,但它会导致内存泄漏:https://codepen.io/jjd/pen/ZEpGGqG为了避免这种情况,需要给所有事件名称加上命名空间并显式注销它们。否则,每次执行代码时都会添加一堆事件监听器,而这些监听器永远不会被清除。$(".myClass").one('transitionend.namespace webkitTransitionEnd.namespace oTransitionEnd.namespace otransitionend.namespace MSTransitionEnd.namespace', function() { $(this).off('.namespace'); //做一些事情 }); - Jesse

61

现代浏览器现在支持无需前缀的事件:

element.addEventListener('transitionend', callback, false);

适用于最新版本的Chrome,Firefox和Safari。甚至也支持IE10+。


16

在Opera 12中,当您使用纯JavaScript进行绑定时,'oTransitionEnd'会起作用:

document.addEventListener("oTransitionEnd", function(){
    alert("Transition Ended");
});

然而,如果您使用jQuery绑定事件,您需要使用'otransitionend'。

$(document).bind("otransitionend", function(){
    alert("Transition Ended");
});

如果你正在使用Modernizr或bootstrap-transition.js,你可以简单地进行更改:

var transEndEventNames = {
    'WebkitTransition' : 'webkitTransitionEnd',
    'MozTransition'    : 'transitionend',
    'OTransition'      : 'oTransitionEnd otransitionend',
    'msTransition'     : 'MSTransitionEnd',
    'transition'       : 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];

你也可以在这里找到一些信息http://www.ianlunn.co.uk/blog/articles/opera-12-otransitionend-bugs-and-workarounds/


6

仅仅出于娱乐目的,请勿实践!

$.fn.transitiondone = function () {
  return this.each(function () {
    var $this = $(this);
    setTimeout(function () {
      $this.trigger('transitiondone');
    }, (parseFloat($this.css('transitionDelay')) + parseFloat($this.css('transitionDuration'))) * 1000);
  });
};


$('div').on('mousedown', function (e) {
  $(this).addClass('bounce').transitiondone();
});

$('div').on('transitiondone', function () {
  $(this).removeClass('bounce');
});

3
如果您只想检测单个转换结束,而不使用任何JS框架,这里有一个小巧方便的实用函数:
function once = function(object,event,callback){
    var handle={};

    var eventNames=event.split(" ");

    var cbWrapper=function(){
        eventNames.forEach(function(e){
            object.removeEventListener(e,cbWrapper, false );
        });
        callback.apply(this,arguments);
    };

    eventNames.forEach(function(e){
        object.addEventListener(e,cbWrapper,false);
    });

    handle.cancel=function(){
        eventNames.forEach(function(e){
            object.removeEventListener(e,cbWrapper, false );
        });
    };

    return handle;
};

使用方法:

var handler = once(document.querySelector('#myElement'), 'transitionend', function(){
   //do something
});

如果您希望在某个时间点取消,您仍然可以使用此方法进行取消。
handler.cancel();

这也适用于其他事件使用情况:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接