在执行事件的时候,jQuery 会根据事件绑定的时候处理来执行事件的逐渐触发,我们观察一组代码:
div.on('mousedown', 'li', function(e) { show('委托到li触发') }) div.on('mousedown', 'ul', function(e) { show('委托到ul触发') }) div.on('mousedown', 'a', function(e) { show('委托到a触发') }) div.on('mousedown', function(e) { show('mousedown') })
给 div 元素绑定一个 mousedown 事件,但是在 div 元素上触发的时候,其实之前还会先触发 li、ul、a,3 个元素的事件,这是因为事件都是绑定的四个事件,3 个是通过委托到 div 元素上触发的,那么这个委托是如何处理的?
设计的思路解析:
委托的实现说起来很简单,我们事件对象里面不是有一个 target 属性吗? target 就指的触发的目标对象。
target 的理解:
<ul> <li>点击执行委托链</li> </ul>
通过 target 与实际的事件绑定对象我们就可以划分一个区域段,通过递归获取每一个元素的 parentNode 节点,在每一个节点层上通过与委托节点的对比用来确定是不是委托的事件元素,这个就是委托的核心思路了
这个处理 jQuery 交给了 $.event.handlers 方法,就上一个结构我们 handlers 这样分解
简单来说就是把 target 到根节点 div 通过 node.parentNode 遍历一遍,然后找到对应的委托元素节点,如果符合就缓存起来用于之后的操作,可以通过 jQuery.event.handlers 方法我们可以获取类似这种的一组数据结构
那么过滤之后的结构就是这样了:通过 handlerQueue 保存需要的委托队列数据
从这里我们可以看出 delegate 绑定的事件和普通绑定的事件是如何分开的,对应一个元素一个 event.type 的事件,处理对象队列在缓存里只有一个,按照冒泡的执行顺序与元素的从内向外递归以及 handlers 的排序,所以就处理了,就形成了事件队列的委托在前,自身事件在后的顺序,这样也跟浏览器事件执行的顺序一致了。
区分delegate绑定和普通绑定的方法是:delegate 绑定从队列头部推入,而普通绑定从尾部推入,通过记录 delegateCount 来划分,delegate 绑定和普通绑定
我们按照委托的顺序遍历这个结构
while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) { event.currentTarget = matched.elem; j = 0; while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) { ret = handleObj.handler.apply(matched.elem, args); //如果返回了false if (ret !== undefined) { if ((event.result = ret) === false) { event.preventDefault(); event.stopPropagation(); }
因为结构上来说,同一个 div 上绑定多个委托元素,那么事件对象引用就是同样的,event.isPropagationStopped 引用永远是 div 的事件对象 div,ul与 p 都是共用的,只是在不同的元素上面修改delegateTarget 与 currentTarget,所以在之前重写的事件对象就发挥作用了,如果在一个元素上调用了stopPropagation 那么后面的事件自然都不会触发了,因为 event.isPropagationStopped 会获取这个状态。
总的来说 jQuery.event.handlers 做的事情:
请验证,完成请求
由于请求次数过多,请先验证,完成再次请求
打开微信扫码自动绑定
绑定后可得到
使用 Ctrl+D 可将课程添加到书签
举报