Jak znormalizować funkcje przejścia CSS3 w różnych przeglądarkach?


91

Zdarzenie końcowe przejścia Webkit nazywa się webkitTransitionEnd, Firefox to przejścieEnd, opera to oTransitionEnd. Jaki jest dobry sposób radzenia sobie z nimi wszystkimi w czystym JS? Czy powinienem wąchać przeglądarkę? czy wdrożyć każdy z nich osobno? W inny sposób nie przyszło mi do głowy?

to znaczy:

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

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

lub

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

function fn() {
   //do whatever
}

W jakim celu jest fałsz?
zadzwoń do mnie

Odpowiedzi:


166

Jest technika używana w Modernizr, ulepszona:

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'; 
}

Następnie możesz po prostu wywołać tę funkcję, gdy potrzebujesz zdarzenia zakończenia przejścia:

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

3
oTransitionEnd został zmniejszony do otransitionend w Operze. Zobacz opera.com/docs/specs/presto2.10/#m274
vieron

1
jest to teraz również przejście na wszystkie małe litery. Zobacz dev.w3.org/csswg/css3-transitions/#transition-events
gossi

1
Usunąłem bit MsTransition, ale resztę odpowiedzi pozostawię bez zmian. Obecne wersje wszystkich głównych przeglądarek innych niż WebKit nie wymagają przedrostka dostawcy. transitioni transitionendwystarczy. Zobacz: caniuse.com/#search=transitions
webinista

4
Dlaczego trzeba przedefiniować undefined?
Atav32

1
@ Atav32, też się nad tym zastanawiam. Jedyne, co przychodzi mi do głowy, to to, że jest tam na wypadek, gdyby ktoś już przedefiniował to na coś.
Qtax

22

Zgodnie z komentarzem Matijsa, najłatwiejszym sposobem wykrycia zdarzeń przejścia jest biblioteka, w tym przypadku 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')
});

W javascript bez biblioteki robi się trochę rozwlekły:

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

function callfunction() {
   //do whatever
}

Ten przedostatni nie powinien być camelCased.
wwaawaw

7
dość zabawne, przyszedłem tutaj, ponieważ moi koledzy właśnie odkryli, że w ich kodzie zostało wrzuconych wiele zdarzeń, które wyglądały dokładnie tak, jak ta odpowiedź
depoulo

1
@Duopixel, sprawdź swoją odpowiedź i rozważ jej zmianę, ponieważ generuje dwa zdarzenia w Chrome i Safari (a przynajmniej we wszystkich innych przeglądarkach Webkit oraz starym firefoxie i operze). msTransitionendnie jest tu potrzebny.
Dan

1
Spowoduje to uruchomienie wielu zdarzeń, jeśli przeniesiono więcej niż jedną usługę. Zobacz: stackoverflow.com/a/18689069/740836
Nick Budden

8

Aktualizacja

Poniższy sposób jest bardziej przejrzysty i nie wymaga modernizacji

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

Alternatywnie

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

Jest to oparte na kodzie sugerowanym przez Modernizr, ale z dodatkowym zdarzeniem dla nowszych wersji Opery.

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


1
To świetny sposób, ale wymaga Modernizra. Czy można to napisać prosto, ale bez Modernizr?
alt

2
Wersja jQuery odpala dwa zdarzenia w przeglądarkach opartych na Webkit (przynajmniej).
Dan

2
@Dan, używam jednego zamiast włączonego, więc odpali tylko raz
Tom

Przepraszam, nie zauważyłem, że masz onezamiast on. To było takie oczywiste!
Dan,

8

Jeśli używasz jQuery i Bootstrap $.support.transition.end, zwróci odpowiednie zdarzenie dla bieżącej przeglądarki.

Jest zdefiniowany w Bootstrap i używany w wywołaniach zwrotnych animacji , chociaż dokumentacja jQuery mówi, że nie należy polegać na tych właściwościach:

Chociaż niektóre z tych właściwości są udokumentowane poniżej, nie podlegają one długiemu cyklowi wycofywania / usuwania i mogą zostać usunięte, gdy wewnętrzny kod jQuery przestanie ich potrzebować.

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


2
Będąc najprostszym rozwiązaniem tutaj, szkoda, że ​​ma takie zastrzeżenie.
Ninjakannon

1
Jest dodany w ich kodzie tutaj github.com/twbs/bootstrap/blob/ ...
Tom

6

Od 2015 r. Ta jednolinijka powinna załatwić sprawę (IE 10+, Chrome 1+, Safari 3.2+, FF 4+ i Opera 12 +): -

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

Dołączanie detektora zdarzeń jest proste: -

element.addEventListener(transEndEventName , theFunctionToInvoke);

Cudowne rozwiązanie. Niestety nie powie ci, jeśli transitionendnie jest w ogóle obsługiwany: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; A potem zrób prostą kontrolę: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
Luuuud Kwietnia

Myślę, że należy to sprawdzić osobno: stackoverflow.com/a/29591030/362006
Salman von Abbas

Czy ta odpowiedź dotyczy również teraz? (Styczeń 2016)
Jessica

Właśnie przetestowałem go w IE 11 i zwrócił fałszywy
Jessica

1

Drugi sposób to droga. Tylko jedno z tych zdarzeń zostanie uruchomione w każdej przeglądarce, więc możesz ustawić je wszystkie i zadziała.


1

Oto bardziej przejrzysty sposób

 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

zamknięcie Google zapewnia, że ​​nie musisz tego robić. Jeśli masz element:

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

patrząc na źródło goog.events.eventtype.js, TRANSITIONEND oblicza się, patrząc na 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

Używam takiego kodu (z 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
}

To pozwala mi używać JS do dodawania rzeczy przez określenie vP połączonego z właściwością, a jeśli nie trafił on w przeglądarkę, po prostu używa standardu. Wydarzenia pozwalają mi łatwo związać w ten sposób:

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

Dzięki! Skończyło się na tym, że zrobiłem coś podobnego, ale bez węszenia przeglądarki. Możesz zobaczyć wynik (i kod) tutaj: cssglue.com/cubic . Jedyny problem z rozwiązaniem polega na tym, że - jeśli dostawcy przeglądarek zdecydują się na ujednolicenie swoich zdarzeń przejścia - mogą porzucić swoje prefiksy i przestaną działać (jest to jeszcze mało prawdopodobne). Ale tak, dzięki temu kod jest znacznie bardziej przejrzysty.
methodofaction

Zgadzam się, chciałem zastąpić swoje coś lepszym, ale z drugiej strony podoba mi się ta prostota.
Rich Bradshaw

2
Tyle ile jest warte. Można to zrobić bez wąchania przeglądarki, po prostu robiącobject.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
Matijs

1
Wersja zdarzenia bez prefiksu nosi nazwę transitionendnie TransitionEnd.
mgol

0

nadpisanie 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);

i zastosowanie takie jak:

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

0

Zaakceptowana odpowiedź jest poprawna, ale nie musisz ponownie tworzyć tego elementu raz po raz i ...

Zbuduj zmienną globalną i dodaj funkcje:

(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));
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.