当没有属性更改时,jQuery中未触发CSS转换事件

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

本文关键字:CSS 事件 转换 jQuery 属性      更新时间:2023-09-26

我遇到了一个有趣的案例,W3C CSS Transitions规范或MDN CSS Transitions-doc中没有明确涵盖它,我想我会分享它,因为它会花费我一些时间。

如果将CSS转换后的属性"更改"为相同的值,则不会触发转换后的事件。正如我所想的,我可以理解为什么这会是默认行为,但它很容易给毫无戒心的开发人员带来这样的问题:$("#test").css("opacity", "1").bind("transitionend", doneFn);

在上面的代码中,如果有问题的元素恰好具有不透明度1,那么doneFn将永远不会被调用。另请参阅http://jsfiddle.net/studgeek/Xj8TB/.

要解决这个问题,有没有一个好的解决方法?

您可以将属性值与现有属性值进行比较,但这比听起来更难做到,因为css属性值可以采用多种形式——不同的单位,甚至可以是字符串值,如auto。因此,您确实需要测试对象的实际状态,当然,对于每个属性,需要进行不同的测试。啊。

一个解决方案是制作自己的jQuery方法来设置一个CSS值,该值将为您处理所有事情。它将获取属性的当前值,设置新值,检查该值是否已更改。如果没有,那么它将手动启动完成功能:

// 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);
        }
    }
}

在上面的例子中,它的工作方式如下:

$("#test").transitionTo("opacity", "1", doneFn);

此处的工作示例:http://jsfiddle.net/jfriend00/LHdGR/

这个插件可以扩展为支持传递多个属性的映射,就像.css()也支持的一样。

我唯一能想到的其他选项是读取transtionDuration,并设置一个稍长的超时值。如果transitionEnd事件触发,则取消超时。如果没有触发,则超时可以手动触发transitionEnd事件。这方面的代码可以这样工作:

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);
    }
}

此处的工作示例:http://jsfiddle.net/jfriend00/hxevW/