Come normalizzo le funzioni di transizione CSS3 nei browser?


91

L'evento di fine transizione di Webkit si chiama webkitTransitionEnd, Firefox è TransitionEnd, opera è oTransitionEnd. Qual è un buon modo per affrontarli tutti in puro JS? Devo fare lo sniffing del browser? o implementarli separatamente? Qualche altro modo che non mi è venuto in mente?

cioè:

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

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

o

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

function fn() {
   //do whatever
}

A cosa serve il falso?
— chiamami il

Risposte:


166

C'è una tecnica usata in Modernizr, migliorata:

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

Quindi puoi semplicemente chiamare questa funzione ogni volta che hai bisogno dell'evento di fine transizione:

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

3
oTransitionEnd era minuscolo in otransitionend in Opera. Vedi opera.com/docs/specs/presto2.10/#m274
— vieron

1
ora è anche la transizione in minuscolo. Vedi dev.w3.org/csswg/css3-transitions/#transition-events
— gossi

1
Ho rimosso il bit MsTransition, ma lascerò intatto il resto della risposta. Le versioni correnti di tutti i principali browser non WebKit non richiedono un prefisso del fornitore. transitione transitionendsono sufficienti. Vedi: caniuse.com/#search=transitions
— webinista

4
Perché è necessario ridefinire undefined?
— Atav32

1
@ Atav32, mi chiedo anche questo. L'unica cosa a cui riesco a pensare è che è lì nel caso in cui qualcun altro lo abbia già ridefinito in qualcosa.
— Qtax

22

Come da commento di Matijs, il modo più semplice per rilevare gli eventi di transizione è con una libreria, jquery in questo caso:

$("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')
});

In javascript senza libreria diventa un po 'prolisso:

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

function callfunction() {
   //do whatever
}

Quel penultimo non dovrebbe essere camelCased.
— wwaawaw

7
abbastanza divertente, sono venuto qui perché i miei colleghi hanno appena scoperto che più eventi sono stati lanciati nel loro codice che assomigliava esattamente a questa risposta
— depoulo

1
@Duopixel prova la tua risposta e valuta la possibilità di cambiarla, perché genera due eventi in Chrome e Safari (e almeno in tutti gli altri browser Webkit più i vecchi Firefox e Opera). msTransitionendnon è necessario qui.
— Dan

1
Attiverà più eventi se è stata eseguita la transizione di più di una proprietà. Vedi: stackoverflow.com/a/18689069/740836
— Nick Budden

8

Aggiornare

Quello che segue è un modo più pulito per farlo e non richiede modernizr

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

In alternativa

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

Questo è basato sul codice suggerito da Modernizr, ma con l'evento extra per le versioni più recenti di Opera.

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


1
Questo è un ottimo modo per farlo, ma richiede Modernizr. Può essere scritto semplicemente ma senza Modernizr?
— alt

2
La versione jQuery attiva due eventi nei browser basati su Webkit (almeno).
— Dan

2
@ Dan ne uso uno invece di acceso, quindi si attiverà solo una volta
— Tom

Scusa, non ho notato che hai oneinvece di on. Era così ovvio!
— Dan

8

Se usi jQuery e Bootstrap $.support.transition.endrestituirà l'evento giusto per il browser corrente.

È definito in Bootstrap e utilizzato nei suoi callback di animazione , sebbene i documenti jQuery dicano di non fare affidamento su queste proprietà:

Sebbene alcune di queste proprietà siano documentate di seguito, non sono soggette a un lungo ciclo di deprecazione / rimozione e possono essere rimosse una volta che il codice jQuery interno non ne ha più bisogno.

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


2
Essendo la soluzione più semplice qui, è un vero peccato che abbia un tale avvertimento.
— Ninjakannon

1
È aggiunto nel loro codice qui github.com/twbs/bootstrap/blob/…
— Tom,

6

A partire dal 2015, questo one-liner dovrebbe fare l'affare (IE 10+, Chrome 1+, Safari 3.2+, FF 4+ e Opera 12+): -

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

Collegare il listener di eventi è semplice: -

element.addEventListener(transEndEventName , theFunctionToInvoke);

Bella soluzione. Sfortunatamente non ti dirà se transitionendnon è supportato affatto: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; E poi fai un semplice controllo: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
— Luuuud

Penso che dovrebbe essere controllato separatamente: stackoverflow.com/a/29591030/362006
— Salman von Abbas

Questa risposta vale anche per ora? (Gennaio 2016)
— Jessica

L'ho appena testato in IE 11 ed è risultato falso
— Jessica

1

La seconda è la strada da percorrere. Solo uno di questi eventi si attiverà in ogni browser, quindi puoi impostarli tutti e funzionerà.


1

Ecco un modo più pulito

 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

la chiusura di Google ti assicura di non doverlo fare. Se hai un elemento:

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

guardando l'origine di goog.events.eventtype.js, TRANSITIONEND viene calcolato esaminando l'agente utente:

// 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

Uso codice come questo (con 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
}

Ciò mi consente di utilizzare JS per aggiungere elementi specificando vP concatentato con la proprietà e se non ha colpito un browser utilizza solo lo standard. Gli eventi mi consentono di legare facilmente in questo modo:

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

Grazie! Ho finito per fare qualcosa di simile, ma senza annusare il browser. Puoi vedere il risultato (e il codice) qui: cssglue.com/cubic . L'unico problema con la tua soluzione è che, se i fornitori di browser decidessero di standardizzare i loro eventi di transizione, potrebbero eliminare i loro prefissi e smetterebbero di funzionare (improbabile, ancora). Ma sì, rende il codice molto più pulito.
— metodo di azione

Sono d'accordo, avevo intenzione di sostituire il mio con qualcosa di meglio, ma d'altra parte mi piace la sua semplicità.
— Rich Bradshaw

2
Per quello che vale. Questo può essere fatto senza che il browser object.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
— annusi

1
La versione senza prefisso dell'evento è denominata transitionend, no 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);

e utilizzo come:

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

0

La risposta accettata è corretta ma non è necessario ricreare quell'elemento ancora e ancora e ...

Costruisci una variabile globale e aggiungi le funzioni:

(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));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.