Comment normaliser les fonctions de transition CSS3 dans les navigateurs?

91

L'événement de fin de transition de Webkit est appelé webkitTransitionEnd, Firefox est transitionEnd, l'opéra est oTransitionEnd. Quel est le bon moyen de les aborder tous en pur JS? Dois-je faire du reniflement du navigateur? ou mettre en œuvre chacun séparément? Une autre façon qui ne m'est pas venue à l'esprit?

c'est à dire:

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

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

ou

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

function fn() {
   //do whatever
}
méthode d'action
la source
Dans quel but est le faux?
appelez-moi le

Réponses:

166

Il existe une technique utilisée dans Modernizr, améliorée:

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

Ensuite, vous pouvez simplement appeler cette fonction chaque fois que vous avez besoin de l'événement de fin de transition:

var transitionEnd = transitionEndEventName();
element.addEventListener(transitionEnd, theFunctionToInvoke, false);
webiniste
la source
3
oTransitionEnd était en minuscule en otransitionend dans Opera. Voir opera.com/docs/specs/presto2.10/#m274
vieron
1
c'est aussi transitionend en toutes minuscules maintenant. Voir dev.w3.org/csswg/css3-transitions/#transition-events
gossi
1
J'ai supprimé le bit MsTransition, mais je laisserai le reste de la réponse intact. Les versions actuelles de tous les principaux navigateurs non WebKit ne nécessitent pas de préfixe de fournisseur. transitionet transitionendsuffisent. Voir: caniuse.com/#search=transitions
webinista
4
Pourquoi faut-il redéfinir undefined?
Atav32
1
@ Atav32, je me demande ça aussi. La seule chose à laquelle je peux penser, c'est qu'il est là au cas où quelqu'un d'autre le redéfinirait déjà en quelque chose.
Qtax
22

Selon le commentaire de Matijs, le moyen le plus simple de détecter les événements de transition est d'utiliser une bibliothèque, jquery dans ce cas:

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

En javascript sans bibliothèque, cela devient un peu verbeux:

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

function callfunction() {
   //do whatever
}
méthode d'action
la source
Cet avant-dernier ne devrait pas être camelCased.
wwaawaw
7
assez drôle, je suis venu ici parce que mes collègues viennent de découvrir que plusieurs événements ont été lancés dans leur code qui ressemblait exactement à cette réponse
depoulo
1
@Duopixel, veuillez tester votre réponse et envisager de la modifier, car elle lance deux événements dans Chrome et Safari (et au moins tous les autres navigateurs Webkit, ainsi que les anciens Firefox et Opera). msTransitionendn'est pas nécessaire ici.
Dan
1
Cela déclenchera plusieurs événements si vous avez plus d'une propriété transférée. Voir: stackoverflow.com/a/18689069/740836
Nick Budden
8

Mettre à jour

Ce qui suit est une façon plus propre de le faire, et ne nécessite pas de modernisation

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

Alternativement

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

Ceci est basé sur le code suggéré par Modernizr, mais avec l'événement supplémentaire pour les nouvelles versions d'Opera.

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

À M
la source
1
C'est une excellente façon de le faire, mais cela nécessite Modernizr. Cela peut-il être écrit simplement mais sans Modernizr?
alt
2
La version jQuery déclenche deux événements dans les navigateurs Webkit (au moins).
Dan
2
@Dan J'en utilise un au lieu de sur pour qu'il ne se déclenche qu'une seule fois
Tom
Désolé, je n'ai pas remarqué que vous aviez à la oneplace on. C'était tellement évident!
Dan
8

Si vous utilisez jQuery et Bootstrap $.support.transition.endrenverra le bon événement pour le navigateur actuel.

Il est défini dans Bootstrap et utilisé dans ses rappels d'animation , bien que la documentation jQuery dise de ne pas se fier à ces propriétés:

Bien que certaines de ces propriétés soient documentées ci-dessous, elles ne sont pas soumises à un long cycle de dépréciation / suppression et peuvent être supprimées une fois que le code jQuery interne n'en a plus besoin.

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

Meleyal
la source
2
Étant la solution la plus simple ici, c'est vraiment dommage qu'il y ait une telle mise en garde.
Ninjakannon
1
Il est ajouté dans leur code ici github.com/twbs/bootstrap/blob/…
Tom
6

À partir de 2015, ce one-liner devrait faire l'affaire (IE 10+, Chrome 1+, Safari 3.2+, FF 4+ et Opera 12 +): -

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

Joindre l'écouteur d'événements est simple: -

element.addEventListener(transEndEventName , theFunctionToInvoke);
Salman von Abbas
la source
Belle solution. Malheureusement, il ne vous dira pas si ce transitionendn'est pas du tout pris en charge: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; Et puis faites une simple vérification: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
Luuuud
Je pense que cela devrait être vérifié séparément: stackoverflow.com/a/29591030/362006
Salman von Abbas
Cette réponse s'applique-t-elle aussi maintenant? (Janvier 2016)
Jessica
Je viens de le tester dans IE 11 et il est retourné faux
Jessica
1

La seconde est la voie à suivre. Un seul de ces événements se déclenchera dans chaque navigateur, vous pouvez donc tous les définir et cela fonctionnera.

Léa Verou
la source
1

Voici une manière plus propre

 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";
    }
Nicolas
la source
0

la fermeture de Google s'assure que vous n'avez pas à faire cela. Si vous avez un élément:

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

en regardant la source de goog.events.eventtype.js, TRANSITIONEND est calculé en regardant l'agent utilisateur:

// 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'),
Joe Heyming
la source
0

J'utilise un code comme celui-ci (avec 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
}

Cela me permet d'utiliser JS pour ajouter des éléments en spécifiant vP concatenté avec la propriété, et s'il n'a pas touché un navigateur, il utilise simplement le standard. Les événements me permettent de lier facilement comme ceci:

object.bind(transitionEnd,function(){
    callback();
});
Rich Bradshaw
la source
Merci! J'ai fini par faire quelque chose de similaire, mais sans renifler le navigateur. Vous pouvez voir le résultat (et le code) ici: cssglue.com/cubic . Le seul problème avec votre solution est que, si les fournisseurs de navigateurs décident de standardiser leurs événements de transition, ils risquent de supprimer leurs préfixes et de cesser de fonctionner (peu probable, encore). Mais oui, cela rend le code beaucoup plus propre.
methodofaction
Je suis d'accord, j'avais l'intention de remplacer le mien par quelque chose de mieux, mais d'un autre côté j'aime sa simplicité.
Rich Bradshaw
2
Pour ce que ça vaut. Cela peut être fait sans renifler le navigateur en faisant simplementobject.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
Matijs
1
La version non préfixée de l'événement est nommée transitionend, pas TransitionEnd.
mgol
0

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

et utilisation comme:

$('myDiv').on('transitionend', function() { ... });
Sont Butuv
la source
0

La réponse acceptée est correcte, mais vous n'avez pas à recréer cet élément encore et encore et ...

Construisez une variable globale et ajoutez la (les) fonction (s):

(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));
centurien
la source