javascript - jQuery事件绑定on和off的问题
迷茫
迷茫 2017-05-18 10:51:00
0
3
518

代码如下:

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,可见在mousedown里off掉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学习者快速成长!