最近想要抛弃blog的zepto库呐,把js全用原生写一遍(不作死就不会死
然而一开始就卡住了,blog是pjax的,需要做事件委托,不然就只能重新绑event。
需求
这个我需要解说下,传统的event绑定是直接绑定到指定元素上的,酱紫的话,当元素被抹除并插入新的元素时,event便会一并抹除。
而事件委托则是需要绑定一个proxy event到元素的父元素,父父元素甚至是document上的绑定,触发此proxy event时判断目标元素并运行real event;
相当于jQuery API中on的两个不同用法
传统:$ele.on( events , handler )
持久:$ele.on( events , selector , handler )
这样做有什么好处呢?
1.元素增删时不必再重新绑定event
2.绑定event数少,节省内存
遇到多子元素需要绑定类似event的时候尤其适用。
而不涉及dom增删的元素,还是建议使用直接加event。
实现
现在先假定这里有个 dom 树,它的结构是酱紫的
<div class="a"> <div class="b"> <div class="c"> </div> <div class="c"> </div> </div> <div class="b"> <div class="c"> </div> <div class="c"> </div> </div> </div>
现在要做的是要在 body 上绑定一个代理事件,监听 body 上的 click 事件,当 body 被点击时,会判断点击的元素是否 .b ,是便执行,否便不执行
翻了下Zepto的源码,自己实现了个简单版的,
// By huihui document.body.addEventListener('click', function(e){ var node = e.target; while(node && !node.matches('.b') ){ node === document.body ? node = false : node = node.parentNode; } if (node) console.log('by huihui'); });
matches 方法各个浏览器的有不同的前缀,需要 polyfill ,详见 MDN
跑去问了下巨巨……kookxiang大大和IntPtr大大给出了他们的实现ヽ(´Д`ヽ)(/´Д`)/
kookxiang大大的实现,不过这个实现貌似有一个缺点,就是找不到用户所点击的那个 .b element,这个……
// By kookxiang document.body.addEventListener('click', function(e){ if (e.target.matches('.b, .b *')) { console.log('by kookxiang'); } });
IntPtr大大给的正确实现方法,需要用到 Element.closest 方法, IE 和 Edge 都还暂不支持,用 这个 polyfill 可以兼容 ie9+ (额外再引用一个模块可以兼容到 ie8 详见 readme
// By IntPtr document.body.addEventListener('click', function(e){ if (event.target.closest(".b")) { console.log('by IntPtr'); } });
示例在这里
See the Pen RRyXyB by Hui Hui (@huihuimoe) on CodePen.