看過 上一回 的問題之後,我們接著看 queue()
與 dequeue()
在核心中做了甚麼事情:
jQuery.extend({
queue: function( elem, type, data ) {
if ( !elem ) {
return;
}
type = (type || "fx") + "queue";
var q = jQuery.data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
/// 一般而言,我們無需特別傳入 data 這個數值陣列,因為 queue() 會自動把 elem 本身的動作排入
/// 所以,倘若你沒有指定其他的事件時,他就直接返回以 elem 自身動作為主的排程
/// queue() 你可以把他想成,一種事件能夠依序執行的『排程』
if ( !data ) {
return q || [];
}
/// 相反的,倘若你指定了其他事件,他就會堆入 queue() 的流程裡面
/// 值得注意的是,你追加了多少事件,就增加的多少個 queue() 的排程
if ( !q || jQuery.isArray(data) ) {
q = jQuery.data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
return q;
},
dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ), fn = queue.shift();
// If the fx queue is dequeued, always remove the progress sentinel
/// 移除 queue() 的方式很神,他是先斬後奏,他先取出第一個 queue() 然後判斷是不是 "inprogress"
/// 如果是,繼續往下取,而因為是"一個接著一個"的方式去做,所以下一個儼然就不是 "inprogress"
/// 如果不是,就沒甚麼好說的,繼續往下做
if ( fn === "inprogress" ) {
fn = queue.shift();
}
/// fn 有值嗎?當然有,如果沒有表示 queue() 已經被執行完畢。
/// 如果 type 相符(意同於呼叫一個自訂 queue(),預設是 fx),就在原有的 queue 堆一個 "inprogress" 在前面
/// 最後回呼下一個 fn,然後再次 dequeue(),達到『先斬後奏』的方法。
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift("inprogress");
}
fn.call(elem, function() {
jQuery.dequeue(elem, type);
});
}
}
});
大抵上來說,我們需要停止或是暫停某些效果時,確實是使用 stop(true,true) 是最為妥當的一個方式,更甚是連帶的將你所設定的 queue 排程清空──其實等同於 stop 的第一個 true 的結果──來確保不會發生意外。然而,如果你需要的是暫時停止,那 stop() 的任何設置可能都無法很直覺的使用!
所以,倘若你的動態效果超過兩個步驟以上,需要可以中途停止並且能繼續執行,請不要使用 . 連字號來使用 queue() 與 dequeue() 這兩個方法,除非你 不要停!不要停!不要停! 不想停!不想停!不想停!老師叫你停你都沒有再聽,你看看現在停不下來了吧(丟滑鼠)。
- 原生
queue()
與dequeue()
因為先斬後奏,所以要由原生 jQuery 來做到是不可能的。 - 使用
.
連字號也是不可暫停的。
那我要怎麼暫停?
-
將動態效果拆入陣列中,當作是 data 傳入
queue()
。 -
將動態效果陣列另存成一個陣列(例如:myQueue),他是可變動的。
-
呼叫
dequeue()
──動態效果開始的每一個步驟──之前,紀錄下陣列鍵值。 -
請用下列方式去調停
queue()
:
var $box = $('#div-a');
$box.queue([]);
$box.each(function() {
var timers = jQuery.timers;
// go in reverse order so anything added to the queue during the loop is ignored
for ( var i = timers.length - 1; i >= 0; i-- ) {
if ( timers[i].elem === this ) {
timers.splice(i, 1);
}
}
});
- 修改自原生
stop()
區塊,但是不強制使用dequeue()
,這是唯一的區別。當在次按下開始的時候,將 myQueue 中,小於該步驟紀錄鍵值的 data 全數shift()
掉,再將這個新的 myQueue 傳入。 - 往後的步驟都是這樣做,直到整個 myQueue 被執行完畢。
範例呢?在電鍋裡,請自己盛!