One workflow is to create your own jQuery method to set a CSS value that will handle everything for you. It will get the current value of the property, set a new value, check if the value has changed. If this did not happen, then he would launch the completion function manually:
// globally set this to the right transition event for the current browser // modernizr has ways of using feature detection to know which is the right way to set this var transitionEvent = "webkitTransitionEnd"; jQuery.fn.transitionTo = function(prop, value, completeFn) { var origValue, item; for (var i = 0, len = this.length; i < len; i++) { item = jQuery(this[i]); origValue = item.css(prop); item.css(prop, value); // if value hasn't changed if (origValue == item.css(prop)) { completeFn.apply(this[i]); } else { this.one(transitionEvent, completeFn); } } }
In the above example, it will work as follows:
$("#test").transitionTo("opacity", "1", doneFn);
A working example is here: http://jsfiddle.net/jfriend00/LHdGR/
This plugin can be expanded to support the transfer of several properties, such as .css() support.
The only other possibility I can think of is to read transtionDuration and set the timeout value a little longer than that. If the transitionEnd event fires, you cancel the timeout. If it does not fire, a timeout may manually trigger the transitionEnd event. The code for this can work as follows:
var transitionEvent = "webkitTransitionEnd"; var transitionDuration = "WebkitTransitionDuration"; // get transition duration in decimal seconds // this only returns the first transition time if there are multiple ones specified jQuery.fn.getDuration = function() { var val = this.css(transitionDuration); if (!val) { val = "0s"; } var num = parseFloat(val); var units = val.replace(/\d\., /g, ""); if (units.indexOf("ms") == 0) { num /= 1000; } return(num); } // set a guaranteed transition event that will always fire, even if // no CSS transition is triggered jQuery.fn.setTransitionEvent = function(completeFn) { var item, duration; for (var i = 0, len = this.length; i < len; i++) { item = jQuery(this[i]); duration = Math.ceil((Number(item.getDuration()) + 0.1) * 1000); (function(t, o) { var timeout = setTimeout(function() { completeFn.apply(o); o.unbind(transitionEvent); }, duration); o.one(transitionEvent, function() { clearTimeout(timeout); }); })(duration, item); } }
A working example is here: http://jsfiddle.net/jfriend00/hxevW/