如何跨浏览器标准化CSS3 Transition功能?


91

Webkit的过渡结束事件称为webkitTransitionEnd,Firefox为transitionEnd,歌剧为oTransitionEnd。用纯JS解决所有问题的好方法是什么?我应该浏览器嗅探吗?还是分别实施?我还没有发生过其他事情?

即:

//doing browser sniffing
var transitionend = (isSafari) ? "webkitTransitionEnd" : (isFirefox) ? "transitionEnd" : (isOpera) ? "oTransitionEnd";

element.addEventListener(transitionend, function(){
  //do whatever
},false);

要么

// Assigning an event listener per browser
element.addEventListener("webkitTransitionEnd", fn);
element.addEventListener("oTransitionEnd", fn);
element.addEventListener("transitionEnd", fn);

function fn() {
   //do whatever
}

错误是出于什么目的?

Answers:


166

Modernizr中使用了一项技术,对它进行了改进:

function transitionEndEventName () {
    var i,
        undefined,
        el = document.createElement('div'),
        transitions = {
            'transition':'transitionend',
            'OTransition':'otransitionend',  // oTransitionEnd in very old Opera
            'MozTransition':'transitionend',
            'WebkitTransition':'webkitTransitionEnd'
        };

    for (i in transitions) {
        if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) {
            return transitions[i];
        }
    }

    //TODO: throw 'TransitionEnd event is not supported in this browser'; 
}

然后,只要需要过渡结束事件,就可以调用此函数:

var transitionEnd = transitionEndEventName();
element.addEventListener(transitionEnd, theFunctionToInvoke, false);

3
oTransitionEnd在Opera中小写为otransitionend。参见Opera.com/docs/specs/presto2.10/#m274
维伦

1
现在它也是所有小写字母的transitionend。参见dev.w3.org/csswg/css3-transitions/#transition-events
gossi 2012年

1
我删除了MsTransition位,但是将其余答案保持不变。所有主要的非WebKit浏览器的当前版本都不需要供应商前缀。transition并且transitionend足够了。参见:caniuse.com/#search=transitions
webinista

4
为什么需要重新定义undefined
Atav32

1
@ Atav32,我也想知道。我唯一能想到的就是它在那儿,以防别人将其重新定义为某种东西。
Qtax 2015年

22

根据Matijs的评论,检测过渡事件的最简单方法是使用库,在这种情况下为jquery:

$("div").bind("webkitTransitionEnd.done oTransitionEnd.done otransitionend.done transitionend.done msTransitionEnd.done", function(){
  // Unlisten called events by namespace,
  // to prevent multiple event calls. (See comment)
  // By the way, .done can be anything you like ;)
  $(this).off('.done')
});

在无库JavaScript中,它有点冗长:

element.addEventListener('webkitTransitionEnd', callfunction, false);
element.addEventListener('oTransitionEnd', callfunction, false);
element.addEventListener('transitionend', callfunction, false);
element.addEventListener('msTransitionEnd', callfunction, false);

function callfunction() {
   //do whatever
}

倒数第二个不应该骆驼。
wwaawaw

7
有趣的是,我来这里是因为我的同事们刚刚发现他们的代码中引发了多个事件,这些事件看起来就像是这个答案
depoulo

1
@Duopixel请测试您的答案并考虑更改它,因为它会在Chrome和Safari(以及至少所有其他Webkit浏览器以及旧的Firefox和Opera)中引发两个事件。msTransitionend这里不需要。
2014年

1
如果您转换了多个属性,它将触发多个事件。请参阅:stackoverflow.com/a/18689069/740836
Nick Budden

8

更新资料

以下是一种更清洁的方法,不需要modernizr

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

或者

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

这是基于Modernizr建议的代码,但具有适用于较新版本Opera的额外事件。

http://modernizr.com/docs/#prefixed


1
这是一种很好的方法,但是需要Modernizr。可以简单地编写但不需要Modernizr吗?
alt

2
jQuery版本会在基于Webkit的浏览器中触发两个事件(至少)。
2014年

2
@Dan我用一个代替,所以它只会发射一次
Tom

对不起,我没注意到你有one代替on。太明显了!
2014年

8

如果使用jQuery,Bootstrap $.support.transition.end将为当前浏览器返回正确的事件。

在Bootstrap定义,并在其动画回调中使用,尽管jQuery文档说不依赖于以下属性:

尽管下面记录了其中一些属性,但是它们不受较长的弃用/删除周期的限制,一旦内部jQuery代码不再需要它们,则可以将其删除。

http://api.jquery.com/jQuery.support/


2
作为这里最简单的解决方案,真是太可惜了!
Ninjakannon

1
它已添加到他们的代码中github.com/twbs/bootstrap/blob/…–
汤姆(Tom)

6

从2015年起,这笔单线交易就可以达成交易(IE 10 +,Chrome 1 +,Safari 3.2 +,FF 4+和Opera 12+):

var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : 'transitionend'

附加事件监听器很简单:

element.addEventListener(transEndEventName , theFunctionToInvoke);

可爱的解决方案。不幸的是,它不会告诉您是否transitionend根本不支持: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; 然后做一个简单的检查: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
Luuuud 2015年


这个答案现在也适用吗?(2016
杰西卡

刚刚在IE 11中对其进行了测试,结果返回false
Jessica

1

第二是要走的路。这些事件中只有一个会在每个浏览器中触发,因此您可以设置所有事件,它便会起作用。


1

这是一种更清洁的方法

 function transitionEvent() {
      // Create a fake element
      var el = document.createElement("div");

      if(el.style.OTransition) return "oTransitionEnd";
      if(el.style.WebkitTransition) return "webkitTransitionEnd";
      return "transitionend";
    }

0

谷歌关闭确保您不必这样做。如果您有一个元素:

goog.events.listen(element, goog.events.EventType.TRANSITIONEND, function(event) {
  // ... your code here
});

查看goog.events.eventtype.js的源代码,可以通过查看useragent来计算TRANSITIONEND:

// CSS transition events. Based on the browser support described at:
  // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
  TRANSITIONEND: goog.userAgent.WEBKIT ? 'webkitTransitionEnd' :
      (goog.userAgent.OPERA ? 'oTransitionEnd' : 'transitionend'),

0

我使用这样的代码(使用jQuery)

var vP = "";
var transitionEnd = "transitionend";
if ($.browser.webkit) {
    vP = "-webkit-";
    transitionEnd = "webkitTransitionEnd";
} else if ($.browser.msie) {
    vP = "-ms-";
} else if ($.browser.mozilla) {
    vP = "-moz-";
} else if ($.browser.opera) {
    vP = "-o-";
    transitionEnd = "otransitionend"; //oTransitionEnd for very old Opera
}

这使我可以使用JS通过指定与属性关联的vP来添加内容,并且如果它未在浏览器中运行,则仅使用标准。这些事件使我可以像这样轻松地进行绑定:

object.bind(transitionEnd,function(){
    callback();
});

谢谢!我最终做了类似的事情,但没有浏览器嗅探。您可以在以下位置查看结果(和代码):cssglue.com/cubic。解决方案的唯一问题是-如果浏览器供应商决定对过渡事件进行标准化处理-他们可能会删除其前缀,并且它们将停止工作(目前不太可能)。但是,是的,它使代码更加简洁。
methodofaction

我同意,我一直打算用更好的东西来代替我的东西,但另一方面,我喜欢它的简单性。
Rich Bradshaw

2
物有所值。只需这样做,就可以在不引起浏览器嗅探的情况下完成object.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
Matijs

1
事件的非前缀版本名为transitionend,而不是TransitionEnd
mgol 2013年

0

jQuery覆盖:

(function ($) {
  var oldOn = $.fn.on;

  $.fn.on = function (types, selector, data, fn, /*INTERNAL*/ one) {
    if (types === 'transitionend') {
      types = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd';
    }

    return oldOn.call(this, types, selector, data, fn, one);
  };
})(jQuery);

和用法如下:

$('myDiv').on('transitionend', function() { ... });

0

接受的答案是正确的,但是您不必一次又一次地重新创建该元素。

构建一个全局变量并添加函数:

(function(myLib, $, window, document, undefined){

/**
 * @summary
 * Returns the browser's supported animation end event type.
 * @desc
 * @see {@link https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/}
 * @function myLib.getAnimationEndType
 * @return {string} The animation end event type
 */
(function(){
   var type;

   myLib.getAnimationEndType = function(){
      if(!type)
         type = callback();
      return type;

      function callback(){
         var t,
             el = document.createElement("fakeelement");

         var animations = {
            "animation"      : "animationend",
            "OAnimation"     : "oAnimationEnd",
            "MozAnimation"   : "animationend",
            "WebkitAnimation": "webkitAnimationEnd"
         }

         for (t in animations){
            if (el.style[t] !== undefined){
               return animations[t];
            }
         }
      }
   }
}());

/**
 * @summary
 * Returns the browser's supported transition end event type.
 * @desc
 * @see {@link https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/}
 * @function myLib.getTransitionEndType
 * @return {string} The transition end event type
 */
(function(){
   var type;

   myLib.getTransitionEndType = function(){
      if(!type)
         type = callback();
      return type;

      function callback(){
         var t,
             el = document.createElement("fakeelement");

         var transitions = {
            "transition"      : "transitionend",
            "OTransition"     : "oTransitionEnd",
            "MozTransition"   : "transitionend",
            "WebkitTransition": "webkitTransitionEnd"
         }

         for (t in transitions){
            if (el.style[t] !== undefined){
               return transitions[t];
            }
         }
      }
   }
}());

}(window.myLib = window.myLib || {}, jQuery, window, document));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.