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.