riskers / blog

:pencil2: 博客写在 Issues 里
http://riskers.github.io/
MIT License
1.13k stars 96 forks source link

Zepto事件委托的小坑 #14

Open riskers opened 8 years ago

riskers commented 8 years ago

问题

今天同事(妹子)遇到一个 Zepto 的事件委托的问题来问我,我当时也懵了,后来解决了。问题还是比较坑的,拿出来分享一下。先看看是什么问题:

页面1

自己解决

为什么?!为什么事件委托在 .a 上可是却也触发了 .b 上的委托。看着妹子求知的眼神,我胸中一阵气短。猜想着是 .a 委托事件最后换了 class,DOM立刻更改了,就在 .a 事件后触发了 .b。所以我立刻让她这样改一下就可以延缓DOM更改:

$doc.on('click','.a',function(){
    alert('a事件')
        var $this = $(this) ;
    setTimeout(function(){
        $this.removeClass('a')
        .addClass('b')  
    },30)
})

然后就正常了 页面2

刨根问底

虽然妹子对我一阵赞许,可是我心里还是隐隐不安,回来通过咨询大牛和看源码知道了这是什么原因。

先看看这个页面 页面3

查看源码我们可以看到,页面3页面1 几乎一模一样,就是在 .a.b 的事件委托顺序不一样:

页面1

页面3

那为什么 页面3 就可以正常呢?就是因为 Zepto 的事件委托和我们想象中的事件委托是不一样的。

Zepto 的事件委托是:

在代码解析的时候,所有document的所有 click 委托事件都依次放入一个队列里,click 的时候先看当前元素是不是.a,符合就执行,然后查看是不是.b,符合就执行。

这样的话,就导致如果 .a 的事件在前面,会先执行 .a 事件,然后 class 更改成 bZepto再查看当前元素是不是 .b,以此类推。

这就是 页面1 出现BUG的原因,而 页面2 之所以也能解决这个问题是因为 class 变化实在延迟之后,click 事件当时没有检测到 .b

看看 Zepto 的事件部分是怎么写的。可以看到是用$this.each 循环绑定在 $this 上的事件。对应在我们的例子,就是 document 上绑定的事件都被塞进一个队列中。

再看看 jQuery 的事件委托:

document上委托了2个 click 事件,click 后判断是否当前符合条件(选择符),然后把事件拿出来执行。

这是符合我们一般的认知的,也是那个妹子那样写代码的原因。你不妨把页面1的 Zepto 换成 jQuery 看看。

这是一个 ZeptojQuery 不同的地方,以后要注意了。


向我捐助 | 关于我 | 工作机会


finas commented 8 years ago

个人理解,委托是一个选择器和事件的map。selector=>[events]。zepto的委托是遍历selector立刻执行对应的events,当匹配到.a后执行events然后下一个匹配,同时匹配到了.b继续执行events。jQuery的委托是遍历selector如果匹配,push events到匹配元素的等待执行函数里。等待遍历完所有selectors再执行等该执行函数。不知道理解的是否有误。

riskers commented 8 years ago

@FINAs 是的

Zepto 的实现就像排队,先排的先执行

jQuery 是把所有事件都一起执行的

cssmagic commented 8 years ago

谢谢分享!

riskers commented 8 years ago

@cssmagic 谢谢魔法哥 :smile:

xbzhs commented 7 years ago

学习了

codezyc commented 7 years ago

shiiiiiiji commented 6 years ago

博客形式很赞!

fatfish516 commented 5 years ago

感谢大佬分享

ray-toby commented 5 years ago