Как нормализовать функции перехода CSS3 в браузерах?

91

Событие завершения перехода Webkit называется webkitTransitionEnd, Firefox - transitionEnd, Opera - 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
}
метод действия
источник
С какой целью ложь?
перезвоните мне

Ответы:

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 был преобразован в otransitionend в Opera. См opera.com/docs/specs/presto2.10/#m274
vieron
1
он также теперь завершается переходом в нижнем регистре. См. Dev.w3.org/csswg/css3-transitions/#transition-events
gossi
1
Я удалил бит MsTransition, но оставлю остальную часть ответа нетронутой. Текущие версии всех основных браузеров, не поддерживающих WebKit, не требуют префикса поставщика. transitionи transitionendхватит. См .: caniuse.com/#search=transitions
webinista
4
Зачем это нужно переопределять undefined?
Atav32
1
@ Atav32, мне тоже интересно. Единственное, что я могу придумать, это то, что он есть на тот случай, если кто-то еще переопределит его на что-то уже.
Qtax 04
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
как ни странно, я пришел сюда, потому что мои коллеги только что обнаружили, что в их код было добавлено несколько событий, которые выглядели точно так же, как этот ответ
Депуло
1
@Duopixel, пожалуйста, проверьте свой ответ и подумайте об изменении его, потому что он вызывает два события в Chrome и Safari (и, по крайней мере, во всех других браузерах Webkit, а также в старых версиях Firefox и Opera). msTransitionendздесь не нужен.
Дэн
1
Он вызовет несколько событий, если у вас будет перенесено более одного свойства. См .: stackoverflow.com/a/18689069/740836
Ник Бадден,
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 (как минимум).
Дэн
2
@Dan, я использую один вместо него, поэтому он срабатывает только один раз
Том
Извините, я не заметил, что у вас oneвместо on. Это было так очевидно!
Дэн
8

Если вы используете jQuery, и Bootstrap $.support.transition.endвернет правильное событие для текущего браузера.

Он определен в Bootstrap и используется в обратных вызовах анимации , хотя в документации jQuery сказано, что на эти свойства нельзя полагаться:

Хотя некоторые из этих свойств задокументированы ниже, они не подлежат длительному циклу устаревания / удаления и могут быть удалены, как только внутренний код jQuery больше не нуждается в них.

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

мелея
источник
2
Это самое простое решение, и очень жаль, что в нем есть такая оговорка.
Ninjakannon
1
Это добавлено в их код здесь github.com/twbs/bootstrap/blob/…
Том
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)
Лууууд,
Думаю, это следует проверить отдельно: stackoverflow.com/a/29591030/362006
Салман фон Аббас
Относится ли этот ответ и к настоящему моменту? (Янв 2016)
Jessica
Только что протестировал его в IE 11, и он вернул false
Джессика
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

закрытие Google гарантирует, что вам не нужно этого делать. Если у вас есть элемент:

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

глядя на источник goog.events.eventtype.js, 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
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));
центурианский
источник