CSS transition event does not fire in jQuery when there is no property change

I came across an interesting case that is not explicitly described in the W3C CSS Transitions or MDN CSS Transitions doc, which I thought I would share because it cost me a little time.

If you "change" a property with the CSS transition to the same value, then no event will be fired. I think I can understand why this will be the default behavior, but it can easily cause problems for an unsuspecting developer with something like this: $("#test").css("opacity", "1").bind("transitionend", doneFn);

In the above code, if this element has an opacity of 1, then doneFn will never be called. Also see http://jsfiddle.net/studgeek/Xj8TB/ .

To do this, is there a good way to solve this problem?

You can compare a property value with an existing property value with an existing property value, but it is more complicated than it seems to do it well, because css property values ​​can take so many forms - different units and even string values ​​like auto. Thus, you really need to check the actual state of the objects, which, of course, must be done for each property. Ugh.

+4
source share
1 answer

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/

+3
source

Source: https://habr.com/ru/post/1399566/


All Articles