Question

I'm implementing an excellent solution (found here) to use a callback function a la jQuery when using CSS transitions.

The problem is that if I use vendor prefixes, Chrome at least binds two events: one for webkitTransitionEnd and the second one for transitionend and, of course, fires the callback twice. Here's my piece of code:

jQuery("#main").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
  console.log("POUM!");
});

Am I doing something wrong?

Was it helpful?

Solution

You're not doing anything wrong. Chrome just uses both the prefixed and un-prefixed versions.

There are a couple options:

  1. Using an outside variable.

    var fired = false;
    jQuery("#main").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
        if ( ! fired ) {
            fired = true;
            console.log("POUM!");
        }
    });
    
  2. Using some kind of detection to get a single variable for transitionend (the below uses Modernizr, and is taken from their documentation):

    var transitionend = (function(transition) {
         var transEndEventNames = {
             'WebkitTransition' : 'webkitTransitionEnd',// Saf 6, Android Browser
             'MozTransition'    : 'transitionend',      // only for FF < 15
             'transition'       : 'transitionend'       // IE10, Opera, Chrome, FF 15+, Saf 7+
        };
    
        return transEndEventNames[transition];
    })(Modernizr.prefixed('transition'));
    
    // then
    jQuery("#main").one(transitionend, function(e) {
        console.log("POUM!");
    });
    

NOTE:

Safari 6 seems to trigger onload for anything that is set in the CSS. So, if you have (assuming all prefixes)

#main {
    width: 40px;
    height: 40px;
    transition: all 200ms;
}

Safari will trigger the transitionend with width and height on load. There are a couple ways to get around this:

  • Use more specific transition-property (but if you set that in the CSS, it will still trigger)
  • Do the following in javascript (it's not the prettiest thing, but it should take care of that edge case and it still works in Chrome) fiddle

    var transitionProperty = 'background-color',
        startColor = jQuery("#main").on(transitionend, function(e) {
            var el = $(this);
            if ( transitionProperty === e.originalEvent.propertyName && el.css(transitionProperty) !== startColor ) {
                console.log("POUM!");
                // This is to make it only happen once.
                $(this).off(transitionend);
            }
        }).css(transitionProperty);
    

OTHER TIPS

I had the same problem, with Chrome firing twice, once for "transitionend" and again for "webkitTransitionEnd". With inspiration from remyabel's solution, I ended up with something fairly simple.

jQuery("#main").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
$(this).off("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd    transitionend");
  console.log("POUM!");
});

Not sure if this addresses your issues, but from this link: http://ianlunn.co.uk/articles/opera-12-otransitionend-bugs-and-workarounds/

It quote says:

Yup, six! In Opera 11, the transitionEnd event fired twice for every one transition ending. In Opera 12, a transitionEnd event will fire six times whether binding via JavaScript or jQuery.

This is for Opera, but I'm assuming that the same problem applies to you. It then goes to say that you can alleviate this problem by doing:

$(document).bind("otransitionend", function(){
    $(this).unbind("otransitionend");
    alert("Transition Ended");
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top