委托模式就是利用事件的冒泡机制把一个或者一组元素的事件委托到它的父层或者更外层元素上,委托模式不属于一般定义的23
种设计模式的范畴,而通常将其看作广义上的技巧型设计模式。
事件委托,委托模式就是利用事件的冒泡机制把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
举个例子,比如一个宿舍的同学同时快递到了,一种方法就是他们都一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一一分发给每个宿舍同学。在这里,取快递就是一个事件,每个同学指的是需要响应事件的DOM
元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。
通常来说,事件主要分为捕获与冒泡两种,IE8
及之前的版本是不支持捕获事件的,W3C
制定的DOM2
事件模型是标准模型,支持捕获型事件和冒泡型事件,调用事件的处理阶段依次为捕获、目标、冒泡。
- 事件捕获
Event Capturing
是一种从上而下的传播方式,以click
事件为例,其会从最外层根节向内传播到达点击的节点,为从最外层节点逐渐向内传播直到目标节点的方式。 - 事件冒泡
Event Bubbling
是一种从下往上的传播方式,同样以click
事件为例,事件最开始由点击的节点,然后逐渐向上传播直至最高层节点。
而事件委托通常使用的是冒泡事件模型,对于事件冒泡,当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到window
,当然其传播的是事件,绑定的执行函数并不会传播,如果父级没有绑定事件函数,就算传递了事件,也不会有什么表现,但事件确实传递了。事件冒泡的原因是事件源本身可能没有处理事件的能力,即处理事件的函数并未绑定在该事件源上。它本身并不能处理事件,所以需要将事件传播出去,从而能达到处理该事件的执行函数。
举个例子,当接到一个小需求做一个日历功能,需要当用户点击日历的格子时,格子的背景色变成灰色,如果我们为每一个日期元素都绑定一个事件,这样会增加很多事件监听,无意中消耗了内存,尤其是在老版本浏览器例如IE6
、IE7
、IE8
等浏览器事件过多会影响用户体验,而对于这种需求完全可以使用事件委托,将点击事件冒泡到外层的父元素去处理,在父元素取得冒泡的元素再去调整DOM
结构。另外,使用事件委托还可以对于未来的更改有一定的预见性,例如我们有一行文字,点击弹出文字内容,而如果我们再在这行文字下加入一行文字,依旧需要为其添加点击事件才能做到点击弹出效果,如果我们使用事件委托机制,就可以直接添加文字而不需要再为其添加事件去处理点击事件了。此外,在老版本的IE
浏览器由于使用的是引用计数的GC
垃圾回收机制,很有可能出现由于没有对DOM
元素的引用进行显性的清除而导致的内存泄漏,使用事件委托可以在一定程度上解决这个问题。下面是一个简单的事件委托的示例。
<!DOCTYPE html>
<html>
<head>
<title>事件委托</title>
</head>
<style type="text/css">
li{
cursor: pointer;
}
</style>
<body>
<ul id="u1">
<li uid="0">0</li>
<li uid="1">1</li>
<li uid="2">2</li>
<li uid="3">3</li>
<li uid="4">4</li>
<li uid="5">5</li>
<li uid="6">6</li>
<li uid="7">7</li>
<li uid="8">8</li>
<li uid="9">9</li>
</ul>
</body>
<script type="text/javascript">
document.getElementById("u1").addEventListener("click",(e) => {
console.log(e.srcElement.getAttribute("uid"));
})
</script>
</html>
https://github.com/WindrunnerMax/EveryDay
https://zhuanlan.zhihu.com/p/26536815
https://juejin.cn/post/6844904000639205390
https://www.cnblogs.com/birdshome/archive/2006/06/01/ClosureReferences.html