Detecting CSS transition completion

by Vince Aggrippino

HTML

<p>Based on code from <a href="http://blog.alexmaccaw.com/css-transitions">http://blog.alexmaccaw.com/css-transitions</a> and <a href="http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers">http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers</a>

</p>
<div class="element"></div>
<div class="status"></div>

SCSS

body {
    margin: 20px;
}
.element {
    width: 100px;
    height: 100px;
    background: black;
}

div.status {
    border: 1px solid black;
    width: 10em;
    padding: 3px;
    margin: 10px auto;
    text-align: center;
    background: black;
    color: white;
}

div.status:before {
    content: "Not Started";
    display: block;
}

div.status.started {
    background: red;
}
div.status.started:before {
    content: "Started";
}
div.status.complete {
    background: green;
}
div.status.complete:before {
    content: "Complete";
}

JavaScript

var defaults = {
    duration: 400,
    easing: ''
};

var whichTransitionEvent = function(){
    var t;
    var 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];
        }
    }
};

var transition_event = whichTransitionEvent();

$.fn.emulateTransitionEnd = function (duration) {
    var called = false,
    $el = this;
    $(this).one(transition_event, function () {
        called = true;
    });
    var callback = function () {
        if (!called) {
            $($el).trigger(transition_event);
        }
    };
    // Sometimes the event doesn't fire?
    setTimeout(callback, duration);
};

$.fn.transition = function (properties, options) {
    options = $.extend({}, defaults, options);
    properties.transition = 'all ' + options.duration + 'ms ' + options.easing;
    $(this).one(transition_event, options.complete || $.noop);
    $("div.status").toggleClass("started");
    $(this).css(properties);
    $(this).emulateTransitionEnd(options.duration);
};

setTimeout(function () {
    var callback = function () {
        $("div.status").toggleClass("complete");
    };
    $('.element').transition({
        'margin-left': 100,
        'background': 'green'
    }, {
        complete: callback,
        duration: 2000
    });
}, 2000);