6

I'm looking for a cleaner way to feature detect the actual name of the transitionend. I've seen a lot of examples just brute force adding handlers to all the variations. Also, I don't want to have to rely on jQuery (or similar framework).

I'm basically starting with this list and hoping to just bind the best fit (i.e., first in list to match).

var transitionendName,
    events = [
        'transitionend',
        'webkitTransitionEnd',
        'MozTransitionEnd',
        'oTransitionEnd'
    ];

// ^^^^^ your code here

myElem.addEventListener(transitionendName, myHandler, false);

Anyone feel they have a clean solution to this? The same solution presumably will work for animationend events.

Edit: msTransitionEnd and '-ms-' prefixed properties were removed in one of the final IE10 release candidates.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
mckamey
  • 17,359
  • 16
  • 83
  • 116

4 Answers4

4

You can register all the event end names and then trigger a very short CSS transition and see which one fires. Also, IE10, uses transitionend, so there is no browser that uses msTransitionEnd.

Here's an example of how to do that: http://jsfiddle.net/jfriend00/5Zv9m/

var transitionendName,
    events = [
        'transitionend',
        'webkitTransitionEnd',
        'MozTransitionEnd',
        'oTransitionEnd'
    ];

function findTransitionEnd(callback) {
    // add an off-screen element
    var elem = document.createElement("div");
    elem.id = "featureTester";
    document.body.appendChild(elem);

    // clean up temporary element when done
    function cleanup() {
        document.body.removeChild(elem);
        elem = null;
    }

    // set fallback timer in case transition doesn't trigger
    var timer = setTimeout(function() {
        if (!transitionendName) {
            cleanup();
            callback("");
        }
    }, 200);

    // register all transition end names
    for (var i = 0; i < events.length; i++) {
        (function(tname) {
            elem.addEventListener(tname, function() {
                if (!transitionendName) {
                    transitionendName = tname;
                    clearTimeout(timer);
                    cleanup();
                    callback(tname);
                }

            });
        })(events[i]);
    }


    // trigger transition
    setTimeout(function() {
        elem.className = "featureTestTransition";
    }, 1);
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I might as well just continue doing brute force for the first time through and then just make a note of which one returned for future iterations. Explicitly triggering just to feature detect feels dirty. – mckamey Oct 10 '12 at 01:41
  • @McKAMEY - I'm not sure why this feels dirty to you. `Feature detection` is all about setting up a situation and programatically checking how the browser responds. That's all this is doing here. – jfriend00 Oct 10 '12 at 01:53
  • Yes but it is heavy and time sensitive. This type of thing really adds up after a while. When a bunch of these kinds of tests need to check features it delays the page load. – mckamey Oct 10 '12 at 01:59
  • @McKAMEY - this is asynchronous so I doubt it slows down page load in any way. If you want to do feature detection on the end event name, you have to run a transition and see which event name works. If you want to just see which style properties exist and then assume you know which end event name goes with that style property, you can certainly do that, but I didn't think that's what you were asking. – jfriend00 Oct 10 '12 at 02:14
4

Another variation which qualifies for pure feature detection (rather than a purely pragmatic solution) but is more efficient:

var transitionendNames = [
    'transitionend',
    'webkitTransitionEnd',
    'MozTransitionEnd',
    'oTransitionEnd'
];

/**
 * Helper function to bind to the correct transitionend event
 * @param {function} callback The function to call when the event fires
 */
var transitionend = function(elem, callback) {
    var handler = function(e) {
        //console.log('transitionend == '+e.type);

        // test in case multiple were registered before change
        if (transitionendNames) {
            // store the actual name
            var transitionendName = e.type;

            // every other time, bind only to actual event
            transitionend = function(elem, callback) {
                elem.addEventListener(transitionendName, callback, false);
            };

            // flag for any others
            transitionendNames = null;
        }
        return callback.call(elem, e);
    };

    // until an event has been triggered bind them all
    for (var i=0, len=transitionendNames.length; i<len; i++) {
        elem.addEventListener(transitionendNames[i], handler, false);
    }
};
mckamey
  • 17,359
  • 16
  • 83
  • 116
  • Wouldn't it be more efficient to just reassign the `transitionend` function to a new function that just uses a global you set as soon as you know which transition name it is rather than still using the array? – jfriend00 Oct 10 '12 at 16:20
  • Ahhh, much better with the local variable `transitionendName`, than overloading the one global variable and using typeof. – jfriend00 Oct 10 '12 at 16:39
  • I'll give you an upvote even though I have a competing answer. I've concluded that this whole question just has no interested readers and the OP isn't paying much attention either. – jfriend00 Oct 10 '12 at 17:28
  • 2
    Nope, didn't realize you were the OP. It's not very common for the OP to provide multiple answers to their own question - in fact I've never seen it done before. I assumed you were just another answerer. It does appear that you and I are the only ones spending any time on this question. – jfriend00 Oct 10 '12 at 17:34
  • 2
    Well, thanks for the help discussing possibile solutions anyway. – mckamey Oct 10 '12 at 17:37
  • I answer my own questions all the time. [Stack even encourages it.](http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/) – CatDadCode Dec 09 '14 at 18:29
  • @jfriend00 - I also answer my questions all the time, why wouldn't I? If I know how to do something, then I ask the question in here and answer myself, so there will be an online documentation where others could learn. – vsync May 24 '15 at 13:11
  • Remember to remove the unused event listeners, to avoid memory leak. – arve0 Jan 16 '16 at 17:13
3

I know this is old, but I'd just like to add: the style attribute isn't always a good way of detecting the animationend/transitionend prefix. For example, some android browsers use no prefix for transitions but webkitAnimationEnd for the JS event.

  • 1
    I would go as far as to say that the style attribute is NEVER a good way of detecting whether or not the events are prefixed. As of today, the accepted answer is broken at least on iPad/Safari. – Brandon Gano Aug 04 '15 at 20:18
0

Using @jfriend00 's answer/example it becomes easier to create a lighter weight detection.

// cssPrefix is any method which detects the property name
var transition = cssPrefix('transition');

var transitionend = {
    'transition': 'transitionend',
    'webkitTransition': 'webkitTransitionEnd',
    'MozTransition': 'transitionend',
    'OTransition': 'oTransitionEnd'
}[transition];

See it in action: http://jsfiddle.net/mckamey/qWPTg/

mckamey
  • 17,359
  • 16
  • 83
  • 116
  • 1
    This does not actually do a feature test on the `transistionend` event which is what I thought the OP was asking for. This looks for a style property and then assumes that a particular transition end event name goes with that style name. – jfriend00 Oct 10 '12 at 02:12
  • This feature detects the style property (which can be efficiently performed) and uses the corresponding `transitionend` event name. – mckamey Oct 10 '12 at 16:05
  • Plus all of these assume they know *all* the vendor prefixes, so there is some a priori knowledge needed. – mckamey Oct 10 '12 at 16:13
  • I just saw a similar idea in the wild: https://github.com/dmotz/oriDomi/blob/master/oriDomi.js#L80 – mckamey Oct 16 '12 at 20:29
  • Folks need to be careful with those switch statements because IE 10 is NOT using the MS prefixes for CSS3. They are using the standard names without prefix. – jfriend00 Oct 16 '12 at 22:05
  • Then that switch branch will just lie dormant. It doesn't hurt and it technically would cover the IE10 preview releases which *did* use those prefixes. – mckamey Oct 16 '12 at 22:39