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


91

Кінцева подія переходу Webkit називається webkitTransitionEnd, Firefox перехідEnd, 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

У «Модернізрі» використана вдосконалена техніка:

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.03.15

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тут не потрібен.
Ден

1
Це призведе до кількох подій, якщо у вас перенесено більше одного властивості. Див .: stackoverflow.com/a/18689069/740836
Нік Бадден

8

Оновлення

Далі наведено більш чистий спосіб зробити це, і він не вимагає модернізації

$(".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)
Luuuud

Думаю, це слід перевіряти окремо: stackoverflow.com/a/29591030/362006
Сальман фон Аббас

Чи стосується ця відповідь і зараз? (Січень 2016)
Джессіка

Щойно перевірив його в IE 11, і він повернув помилку
Джессіка

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 обчислюється, переглядаючи useragent:

// 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 . Єдина проблема з вашим рішенням полягає в тому, що - якщо постачальники браузерів вирішать стандартизувати свої події переходу - вони можуть скинути свої префікси і вони припинять роботу (поки малоймовірно). Але так, це робить код набагато чистішим.
методофакція

Я згоден, я мав намір замінити своє чимось кращим, але, з іншого боку, мені подобається простота цього.
Річ Бредшоу,

2
За свою ціну. Це можна зробити, не object.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
нюхаючи

1
Версія події без префіксу називається transitionendні TransitionEnd.
mgol

0

jquery override:

(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));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.