Wie normalisiere ich CSS3-Übergangsfunktionen in verschiedenen Browsern?

91

Das Übergangsendeereignis von Webkit heißt webkitTransitionEnd, Firefox ist TransitionEnd, die Oper ist oTransitionEnd. Was ist ein guter Weg, um sie alle in reinem JS anzugehen? Soll ich Browser schnüffeln? oder jedes einzeln implementieren? Ein anderer Weg, der mir nicht in den Sinn gekommen ist?

dh:

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

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

oder

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

function fn() {
   //do whatever
}
Methode der Aktion
quelle
Zu welchem ​​Zweck ist das Falsche?
Rufen Sie mich am

Antworten:

166

In Modernizr wird eine Technik verwendet, die verbessert wurde:

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

Dann können Sie diese Funktion einfach aufrufen, wann immer Sie das Übergangsende-Ereignis benötigen:

var transitionEnd = transitionEndEventName();
element.addEventListener(transitionEnd, theFunctionToInvoke, false);
Webinista
quelle
3
oTransitionEnd wurde in Opera auf otransitionend reduziert. Siehe opera.com/docs/specs/presto2.10/#m274
vieron
1
es ist jetzt auch in Kleinbuchstaben übergangsweise. Siehe dev.w3.org/csswg/css3-transitions/#transition-events
gossi
1
Ich habe das MsTransition-Bit entfernt, werde aber den Rest der Antwort im Takt belassen. Die aktuellen Versionen aller gängigen Nicht-WebKit-Browser erfordern kein Herstellerpräfix. transitionund transitionendsind genug. Siehe: caniuse.com/#search=transitions
Webinista
4
Warum muss es neu definiert werden undefined?
Atav32
1
@ Atav32, das frage ich mich auch. Das einzige, woran ich denken kann, ist, dass es da ist, falls jemand anderes es bereits zu etwas neu definiert hat.
Qtax
22

Laut Matijs Kommentar ist der einfachste Weg, Übergangsereignisse zu erkennen, eine Bibliothek, in diesem Fall 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')
});

In bibliothekslosem Javascript wird es etwas ausführlich:

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

function callfunction() {
   //do whatever
}
Methode der Aktion
quelle
Dieser vorletzte sollte nicht camelCased sein.
Wwaawaw
7
lustig genug, ich bin hierher gekommen, weil meine Kollegen gerade entdeckt haben, dass mehrere Ereignisse in ihren Code geworfen wurden, die genau wie diese Antwort
aussahen
1
@Duopixel Bitte testen Sie Ihre Antwort und überlegen Sie, sie zu ändern, da sie zwei Ereignisse in Chrome und Safari auslöst (und zumindest alle anderen Webkit-Browser sowie alte Firefox- und Opernprogramme). msTransitionendwird hier nicht benötigt.
Dan
1
Es werden mehrere Ereignisse ausgelöst, wenn mehr als eine Eigenschaft übertragen wurde. Siehe: stackoverflow.com/a/18689069/740836
Nick Budden
8

Aktualisieren

Das Folgende ist eine sauberere Methode und erfordert keine Modernisierung

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

Alternative

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

Dies basiert auf dem von Modernizr vorgeschlagenen Code, jedoch mit dem zusätzlichen Ereignis für neuere Versionen von Opera.

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

Tom
quelle
1
Dies ist eine großartige Möglichkeit, erfordert jedoch Modernizr. Kann das einfach aber ohne Modernizr geschrieben werden?
Alt
2
Die jQuery-Version löst (mindestens) zwei Ereignisse in Webkit-basierten Browsern aus.
Dan
2
@Dan ich benutze eine anstelle von auf, so dass es nur einmal feuern wird
Tom
Entschuldigung, ich habe nicht bemerkt, dass Sie onestattdessen haben on. Es war so offensichtlich!
Dan
8

Wenn Sie jQuery verwenden, gibt Bootstrap $.support.transition.enddas richtige Ereignis für den aktuellen Browser zurück.

Es wird in Bootstrap definiert und in seinen Animationsrückrufen verwendet , obwohl die jQuery-Dokumente sagen, dass Sie sich nicht auf diese Eigenschaften verlassen sollen:

Obwohl einige dieser Eigenschaften unten dokumentiert sind, unterliegen sie keinem langen Verfalls- / Entfernungszyklus und können entfernt werden, sobald der interne jQuery-Code sie nicht mehr benötigt.

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

meleyal
quelle
2
Da dies hier die einfachste Lösung ist, ist es eine echte Schande, dass dies eine solche Einschränkung darstellt.
Ninjakannon
1
Es ist in ihrem Code hier hinzugefügt github.com/twbs/bootstrap/blob/…
Tom
6

Ab 2015 sollte dieser Einzeiler den Deal machen (IE 10+, Chrome 1+, Safari 3.2+, FF 4+ und Opera 12+): -

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

Das Anhängen des Ereignis-Listeners ist einfach: -

element.addEventListener(transEndEventName , theFunctionToInvoke);
Salman von Abbas
quelle
Schöne Lösung. Leider wird es Ihnen nicht sagen, ob überhaupt transitionendnicht unterstützt wird: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; Und dann machen Sie eine einfache Überprüfung: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
Luuuud
Ich denke, das sollte separat überprüft werden: stackoverflow.com/a/29591030/362006
Salman von Abbas
Gilt diese Antwort auch jetzt? (Jan 2016)
Jessica
Habe es gerade in IE 11 getestet und es hat falsch zurückgegeben
Jessica
1

Der zweite ist der richtige Weg. In jedem Browser wird nur eines dieser Ereignisse ausgelöst, sodass Sie alle festlegen können und es funktioniert.

Lea Verou
quelle
1

Hier ist ein sauberer Weg

 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";
    }
Nikolaus
quelle
0

Google Closure stellt sicher, dass Sie dies nicht tun müssen. Wenn Sie ein Element haben:

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

Wenn Sie sich die Quelle von goog.events.eventtype.js ansehen, wird TRANSITIONEND anhand des Benutzeragenten berechnet:

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

Ich benutze Code wie diesen (mit 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
}

Auf diese Weise kann ich JS verwenden, um Dinge hinzuzufügen, indem ich vP spezifiziere, das mit der Eigenschaft übereinstimmt, und wenn es keinen Browser trifft, wird nur der Standard verwendet. Die Ereignisse lassen mich einfach so binden:

object.bind(transitionEnd,function(){
    callback();
});
Rich Bradshaw
quelle
Vielen Dank! Am Ende habe ich etwas Ähnliches gemacht, aber ohne Browser zu schnüffeln. Sie können das Ergebnis (und den Code) hier sehen: cssglue.com/cubic . Das einzige Problem mit Ihrer Lösung besteht darin, dass Browser-Anbieter, wenn sie sich entscheiden, ihre Übergangsereignisse zu standardisieren, möglicherweise ihre Präfixe löschen und nicht mehr funktionieren (noch unwahrscheinlich). Aber ja, es macht den Code viel sauberer.
Methodofaction
Ich stimme zu, ich wollte meine durch etwas Besseres ersetzen, aber andererseits gefällt mir die Einfachheit.
Rich Bradshaw
2
Für was es wert ist. Dies kann ohne Browser-Schnüffeln getan werden, indem Sie einfach tunobject.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
Matijs
1
Die nicht vorangestellte Version des Ereignisses heißt transitionend, nicht 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);

und Verwendung wie:

$('myDiv').on('transitionend', function() { ... });
Sind Butuv
quelle
0

Die akzeptierte Antwort ist korrekt, aber Sie müssen dieses Element nicht immer wieder neu erstellen und ...

Erstellen Sie eine globale Variable und fügen Sie die Funktion (en) hinzu:

(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));
Centurian
quelle