灰灰 の blog

灰灰✿喵呼哈哈(>^ω^<)

实现元素的事件委托

2016.7.27 | 2,760阅读 | 0条评论 | JavaScript

最近想要抛弃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 &amp;&amp; !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.



おわり、鸣谢:kookxiang & Intptr

暂无评论

发表评论

表情