[jQuery tech.] 你的 stop() 真的 stop 了嗎 - 漫談 queue() part 1

前陣子例用 queue 做了許多東西,慢慢的發現,原生的 stop() 並非我想像中的樣子。

  • 使用任何的動態效果,或是 delay() 這樣的效果,stop() 能使之停止。
  • 使用 queue()stop() 能使之停止。
  • stop() 可以傳入兩個布林值,一個是 clearQueue,另一個是 JumpToEnd

然而,我卻發現事實上並不是這樣。

  • stop() 僅可停止單一動態動態效果,若連續使用並不會停止後續動作。

    例如:$('div').animate( { 'left' : '500px' } ).delay(1000).animate( { 'left' : '0px' } );


    使用 stop() 僅會停止紅色的區域,綠色區域會繼續執行,很不幸的,他不會停止!

  • clearQueue:true 會清除所有關於動態效果的執行動作。

    例如:$('div').animate(
    { 'left' : '500px' } ).delay(1000).animate( { 'left' : '0px' } );



    使用 stop(true) 停止所有動作,且會停在執行中的結果。但是如果你再次呼叫時,他會從頭開始執行


    問題來了,如果我在 .animate(
    { 'left' : '500px' } )
    之後才按下 stop(true),然後再次呼叫時,因為會從頭開始執行的關係,所以 .animate(
    { 'left' : '500px' } ) 會再執行一次
    ,這是動態效果中不想見到的,因為看起來會像是沒有在動作一樣(因為已經做完了)。


    另外,倘若我在
    .animate(
    { 'left' : '0px' } )
    之後按下 stop(true),然後再次呼叫時,因為從頭開始執行的關係,他會重複上一次的動作,這也是效果中不想見到的,因為應該是繼續最後一個步驟。

  • JumpToEnd:true 會直接結束該段動態效果,並繼續往下執行。

    例如:$('div').animate(
    { 'left' : '500px' } )
    .delay(1000).animate(
    { 'left' : '0px' } )
    ;


    使用 stop(false,true) 直接顯示紅色區域的結果,然後綠色區域會繼續執行,很不幸的,他依舊不會停止!

  • clearQueue:true, JumpToEnd:true 清除所有關於動態效果的執行動作,並結束該段動態效果。
    例如:$('div').animate(
    { 'left' : '500px' } )
    .delay(1000).animate(
    { 'left' : '0px' } )
    ;


    使用 stop(false,true) 直接顯示紅色區域
    的結果,綠色區域會停止執行。但是如果你再次呼叫時,
    他會從頭開始執行。問題跟第 2 點一樣,若再次呼叫時,他會從頭開始執行

  • jQuery 1.4 之後新增了 .clearQuere(),雖然方便,但是跟 stop() 的問題是一樣的。

接著,我來說一下我所預期的 stop() 是怎麼樣的一個情況:

  • stop() 應該停止整段動態效果,所以他應該是 clearQueue:true, JumpToEnd:true 的狀態,至於動態執行的階段問題,應該由動態撰寫者自行決定是否重設。

  • clearQueue:true 應該更謹慎使用!由於會重設整段動態效果,所以執行階段問題一樣是要由撰寫者自行決定是否該重新設定

  • JumpToEnd:true 完全誤導使用者對於 stop() 的概念與本身詞意!也許這是很嚴重的指責,但是我不得不這麼說,當你的動作在被按下 stop() 之後,卻被使用者發現還會執行後續動作,這不是很詭譎嗎!

  • clearQueue:true,
    JumpToEnd:true 應該是預設值,而 stop() 應該被修改甚至消滅!

  • .clearQueue() 並沒有好到哪去。

所以,我們來看看 jQuery 關於 stop() 的核心做了甚麼事情:

stop: function( clearQueue, gotoEnd ) {
        var timers = jQuery.timers;

        /// 剛剛說的,clearQueue: true 時會將整個 queue 清空
        if ( clearQueue ) {
            this.queue([]);
        }

        this.each(function() {
            // 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 ) {

                    /// 當 JumpToEnd:true 時,會直接結束該段 animate 的 setTimeout
                    if (gotoEnd) {
                        // force the next step to be the last
                        timers[i](true);
                    }

                    timers.splice(i, 1);
                }
            }
        });

        // start the next in the queue if the last step wasn't forced

        /// 誰能告訴我,為什麼 JumpToEnd:false (預設值) 的時候,要執行下一個 queue 內容?
        /// 所以我才說,stop() 這個指令的預設值,完全違背了本身的概念與詞意!

        if ( !gotoEnd ) {
            this.dequeue();
        }

        return this;
    }

現在知道問題點了嗎?這個 stop() 的預設值所表現的行為,應該叫做 stopAndPlay() 才對!

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei