var readyTime;
$(function() { readyTime = jQuery.now(); });
$.afterDOMReady = $.createCache(function( defer, delay ) {
delay = delay || 0;
$(function() {
var delta = $.now() - readyTime;
if ( delta >= delay ) { defer.resolve(); }
else {
setTimeout( defer.resolve, delay - delta );
}
});
});
新的afterDOMReady辅助方法用最少的计数器提供了domReady后的适当时机。 如果延迟已经过期,回调会被马上执行。
同步多个动画
动画是另一个常见的异步任务范例。 然而在几个不相关的动画完成后执行代码仍然有点挑战性。尽管在jQuery1.6中才提供了在动画元素上取得promise对象的功能,但它是很容易的手动实现:
复制代码 代码如下:
$.fn.animatePromise = function( prop, speed, easing, callback ) {
var elements = this;
return $.Deferred(function( defer ) {
elements.animate( prop, speed, easing, function() {
defer.resolve();
if ( callback ) {
callback.apply( this, arguments );
}
});
}).promise();
};
然后,我们可以使用$.when()同步化不同的动画:
复制代码 代码如下:
var fadeDiv1Out = $( "#div1" ).animatePromise({ opacity: 0 }),
fadeDiv2In = $( "#div1" ).animatePromise({ opacity: 1 }, "fast" );
$.when( fadeDiv1Out, fadeDiv2In ).done(function() {
/* both animations ended */
});
我们也可以使用同样的技巧,建立了一些辅助方法:
复制代码 代码如下:
$.each([ "slideDown", "slideUp", "slideToggle", "fadeIn", "fadeOut", "fadeToggle" ],
function( _, name ) {
$.fn[ name + "Promise" ] = function( speed, easing, callback ) {
var elements = this;
return $.Deferred(function( defer ) {
elements[ name ]( speed, easing, function() {
defer.resolve();
if ( callback ) {
callback.apply( this, arguments );
}
});
}).promise();
};
});
然后想下面这样使用新的助手代码来同步动画:
复制代码 代码如下:
$.when(
$( "#div1" ).fadeOutPromise(),
$( "#div2" ).fadeInPromise( "fast" )
).done(function() {
/* both animations are done */
});
一次性事件
虽然jQuery提供你可能需要的所有的时间绑定方法,但当事件仅需要处理一次时,情况可能会变得有点棘手。( 与$.one() 不同 )
例如,您可能希望有一个按钮,当它第一次被点击时打开一个面板,面板打开之后,执行特定的初始化逻辑。 在处理这种情况时,人们通常会这样写代码:
复制代码 代码如下:
var buttonClicked = false;
$( "#myButton" ).click(function() {
if ( !buttonClicked ) {
buttonClicked = true;
initializeData();
showPanel();
}
});
不久后,你可能会在面板打开之后点击按钮时添加一些操作,如下:
复制代码 代码如下:
if ( buttonClicked ) { /* perform specific action */ }
这是一个非常耦合的解决办法。 如果你想添加一些其他的操作,你必须编辑绑定代码或拷贝一份。 如果你不这样做,你唯一的选择是测试buttonClicked。由于buttonClicked可能是false,新的代码可能永远不会被执行,因此你 可能会失去这个新的动作。
使用deferreds我们可以做的更好 (为简化起见,下面的代码将只适用于一个单一的元素和一个单一的事件类型,但它可以很容易地扩展为多个事件类型的集合):
复制代码 代码如下:
$.fn.bindOnce = function( event, callback ) {
var element = $( this[ 0 ] ),
defer = element.data( "bind_once_defer_" + event );
if ( !defer ) {
defer = $.Deferred();
function deferCallback() {
element.unbind( event, deferCallback );
defer.resolveWith( this, arguments );
}
element.bind( event, deferCallback )
element.data( "bind_once_defer_" + event , defer );
}
return defer.done( callback ).promise();
};
该代码的工作原理如下:
•检查该元素是否已经绑定了一个给定事件的deferred对象
•如果没有,创建它,使它在触发该事件的第一时间解决
•然后在deferred上绑定给定的回调并返回promise
代码虽然很冗长,但它会简化相关问题的处理。 让我们先定义一个辅助方法:
复制代码 代码如下:
$.fn.firstClick = function( callback ) {
return this.bindOnce( "click", callback );
};
然后,之前的逻辑可以重构如下:
复制代码 代码如下: