javascript - jQuery事件綁定on和off的問題
迷茫
迷茫 2017-05-18 10:51:00
0
3
517

程式碼如下:

var $test = $(document);
  
function handler1() {
  console.log( "handler1" );
  $test.off( "click", handler2 );
}
  
function handler2() {
  console.log( "handler2" );
}
  
$test.on( "click", handler1 );
$test.on( "click", handler2 );

這段程式碼為什麼第一次點擊的時候會輸出handler1和handler2,handler1中的off起作用了但是仍然會執行一次handler2,原因是什麼?
參考jQuery文件

文檔裡解釋的是: Adding or removing event handlers on the current element won't take effect until the next time the event is handled
也許是我英語太渣,我感覺這個地方表述的不是很準確,應該是同一事件的新增或刪除在目前處理過程中無效,例如我把兩個click改成如下

var $test = $(document);
  
function handler1() {
  console.log( "handler1" );
  $test.off( "mouseup", handler2 );
}
  
function handler2() {
  console.log( "handler2" );
}
  
$test.on( "mousedown", handler1 );
$test.on( "mouseup", handler2 );

那就只會輸出handler1,可見在mousedownoff掉mouseup是成功的

回到最開始,為什麼同一事件的新增或刪除在目前處理過程中無效? (我試著找了找jQuery的源碼,比較複雜,放棄了)

迷茫
迷茫

业精于勤,荒于嬉;行成于思,毁于随。

全部回覆(3)
左手右手慢动作

還是得從源碼開始講解

先看這裡: https://github.com/jquery/jqu...

handlerQueue = jQuery.event.handlers.call( this, event, handlers );

此處為事件分發的核心代碼開始處,可見jquery分發事件時需調用this.event.handler 求出事件回調的列表,此處handlerQueue 只是內部事件列表一部分的拷貝,而你用off 更改的是內部事件列表,對拷貝出的handlerQueue 自然沒有影響。

就像是全部的事件回呼函數都存在鍋裡,當有事件發生時,jquery 會把一部回調拷貝,盛到碗裡,然後依次執行碗裡的函數。你 off 改變的只是鍋子裡的函數,碗裡的不受影響。

左手右手慢动作

可以把on可以看成把處理方法加到組件的一個事件處理數組裡,off就是從數組裡去掉這個方法。當事件觸發時,jq就會一次執行事件處理數組裡的方法,那麼如果事件處理方法裡呼叫了off會怎麼樣?文件裡已經寫的很清楚了,在當前事件觸發循環裡並不會立刻從數組裡去掉該處理方法,只有當綁定的事件再觸發時才會體現。

Peter_Zhu

感謝大家
問題找到了,原因在於jq內部對於事件的封裝處理時,同一類型的事件會添加到同一個數組中,而在觸發事件時遍歷又是這個數組的一個複製,off沒有改變複製的數組,所以導致第一次click的時候輸出了hanler2,而對於mousedown和mouseup,因為是不同類型的事件,是封裝成兩個數組的,所以在mousedown中off掉mouseup是有效的。也許表達的不是很好,貼幾行主要的jq源碼

//获取触发的事件的数组,在上面的代码中触发click时这个数组中是有两个值的,而触发mousedown时是只有一个值的,这个值是内部封装的一个handleObj对象
handlers = ((jQuery._data(this, "events") || {})[event.type] || [])

...

//之后会把这个handlers数组根据是否委托来slice(也是这个地方复制了handlers,导致click中的off第一次点击的时候无效),然后添加到数组handlerQueue中
handlerQueue.push({ elem: this, matches: handlers.slice(delegateCount) })

...
//最后触发的时候是遍历handlerQueue中的matches来执行
 for (i = 0; i < handlerQueue.length && !event.isPropagationStopped() ; i++) {
                matched = handlerQueue[i];
                event.currentTarget = matched.elem;

                for (j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped() ; j++) {
                    ...
                    }
}
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!