在我的另外一篇文章 解析 Javascript 事件冒泡机制里的最后, 从冒泡机制的角度谈了一点对 Javascript 事件委托的理解现在单独把事件委托 拿出来和大家探讨一下
什么是委托?
所谓的委托, 现实意义上讲是指将自己的事务嘱托他人代为处理比如说甲委托乙去做某些事儿, 那么, 甲则是委托人, 乙是被委托人真正做事情的是乙, 即被委托人, 而委托人甲 则是把相应的信息传递给被委托人乙, 自己本该做的事情交给了乙来做, 如下图这个例子:
那么在我们 javascript 里, 什么是事件委托呢?
事件委托
事件委托 允许我们不必为某些特定的节点添加事件监听器, 而是将事件监听器添加到 (这些节点的) 某个 parent 节点上事件监听器分析冒泡事件, 去找到匹配的子节点元素, 然后做出相应的事件响应
事件委托具体是怎么工作的呢? 我们从下面的简单的例子开始, 给大家展示事件委托的工作原理
先看下面一段代码:
- [html] view plain copy
- print?
- <div id="box1" class="box1">
- <p id="para1">Paragraph 1</p>
- <p id="para2">Paragraph 2</p>
- <p id="para3">Paragraph 3</p>
- <p id="para4">Paragraph 4</p>
- <p id="para5">Paragraph 5</p>
- </div>
我现在想每点击一次 div1 下的 p 元素时, 弹出 "paraX is clicked." 的提示框, 为此, 我们一般的做法就是给每一个 p 元素增加 相应的事件监听:
- [javascript] view plain copy
- print?
- <script type="text/javascript">
- window.onload = function() {
- // 添加事件监听
- document.getElementById("para1").addEventListener("click",eventPerformed);
- document.getElementById("para2").addEventListener("click",eventPerformed);
- document.getElementById("para3").addEventListener("click",eventPerformed);
- document.getElementById("para4").addEventListener("click",eventPerformed);
- document.getElementById("para5").addEventListener("click",eventPerformed);
- }
- // 事件相应函数
- function eventPerformed(event){
- alert(event.target.id+"is clicked.");
- }
- </script>
是的, 上述的代码完全可以实现上的功能我们思考这个问题, 如果这个 div1 下有非常多个 p 元素, 我们总不能每一个都写上形如 document.getElementById("paraX").addEventListener("click",eventPerformed); 的代码片段吧, 这样的话就太机械了! 另外, 如果在实际的应用中, 很有可能同过 js 在 div1 下动态生成 p 元素, 这时候, 我们相应地还要添加事件处理函数, 像这种动态添加的动作很有可能分散在我们 应用的很多个角落, 这样动态添加处理函数将是一个非常蛋疼的事儿!
解决方法: 利用 事件冒泡传递的机制, 将本来本元素要完成的事件处理逻辑, 委托给 父类节点, 父类节点根据触发事件的节点信息, 执行对应的事件处理逻辑
- [javascript] view plain copy
- print?
- <script type="text/javascript">
- window.onload = function() {
- // 添加事件监听
- document.getElementById("box1").addEventListener("click",eventPerformed);
- }
- // 事件相应函数
- function eventPerformed(event){
- if(event.target.nodeName == 'P')
- {
- alert(event.target.id+"is clicked.");
- }
- }
- </script>
通过上述的代码, 就可以轻松搞定 P 元素的事件处理函数绑定了, 动态添加元素的时候, 不需要再手动地绑定处理函数了
可以看出, 使用事件委托, 可以简化了事件的处理逻辑, 避免了多余的事件处理函数, 进而节约了一定的内存
但是, 事件委托也是有缺点的: 如果现在的 dom 元素分为很多很多层, 对于底层事件的委托, 有可能在事件冒泡的过程中, 中途被某个节点 终止冒泡了, 这样事件就传递不到上层, 则委托就会失败了
jquery 对事件委托的支持
在 jQuery 里对事件委托的支持, 有以下几个函数:
其中, live() 和 die()delegate()和 undelegate() 是成对出现的, 分别是绑定和解绑的过程
delegate(selector,[type],[data],fn)
jquery 的 delegate 方法是典型的为事件委托准备的
看一下相应的定义:
delegate(selector,[type],[data],fn)
selector: 选择器字符串, 用于过滤器触发事件的元素
type: 附加到元素的一个或多个事件由空格分隔多个事件值必须是有效的事件
data: 传递到函数的额外数据
fn: 当事件发生时运行的函数
概述:
指定的元素 (属于被选元素的子元素) 添加一个或多个事件处理程序, 并规定当这些事件发生时运行的函数
使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素)
例如, 我们将上面的例子用 jquery 实现:
- [javascript] view plain copy
- print?
- <script type="text/javascript">
- $(function() {
- // 让 box1 处理来自 子元素 P 的 click 事件委托
- $("#box1").delegate("p", "click", function(event) {
- alert(event.target.id + "is clicked.");
- });
- })
- </script>
上述的代码实现了对 box1 的子元素 P 的 click 事件的委托处理如果我们在 js 中动态地给 box1 增加子元素 P, 相应的处理函数也会对其有效
undelegate([selector,[type],fn])
相应地, 如果想取消对应的事件委托, 可以使用以下代码:
- [javascript] view plain copy
- print?
- $("#box1").undelegate("p","click");
delegate 的使用方式是对某个元素的子元素进行事件委托处理, 即形如:$("parentElement").delegate("siblings","eventType",function); parentElement 元素作为一个冒泡事件处理的被委托者 jQuery 还有另外一种方式: 将元素的事件处理委托给 DOM 根节点来处理, 这种方式是 live()方式:
live(type, [data], fn)
type : 事件类型
data : 附加的额外数据
fn : 相应的处理函数
描述: jQuery 给所有匹配的元素附加一个事件处理函数, 即使这个元素是以后再添加进来的也有效
备注: 自 jQuery1.9 后 就废除这个函数, 只能在 jQuery1.9 以前的版本中使用
将上述的功能用此方法实现:
- [javascript] view plain copy
- print?
- <script type="text/javascript">
- $(function() {
- $("p").live("click",function(event) {
- alert(event.target.id + "is clicked.");
- });
- })
- </script>
- die(type, [fn])
备注: 自 jQuery1.9 后 就废除这个函数, 只能在 jQuery1.9 以前的版本中使用
从元素中删除先前用. live()绑定的所有事件.(此方法与 live 正好完全相反)如果不带参数, 则所有绑定的 live 事件都会被移除
与 live() 相对应, 取消绑定, 则用下列代码:
- [javascript] view plain copy
- print?
- $("p").die();
在事件绑定上, jQuery 提供了一种更通用的函数:
on(events,[selector],[data],fn)
参数:
events: 一个或多个用空格分隔的事件类型和可选的命名空间, 如 "click" 或 "keydown.myPlugin"
selector: 一个选择器字符串用于过滤器的触发事件的选择器元素的后代如果选择的< null 或省略, 当它到达选定的元素, 事件总是触发
data: 当一个事件被触发时要传递 event.data 给事件处理函数
fn: 该事件被触发时执行的函数 false 值也可以做一个函数的简写, 返回 false
将上述的功能改用 on 函数实现:
- [javascript] view plain copy
- print?
- <script type="text/javascript">
- $(function() {
- $("#box1").on("click","p",function(event) {
- alert(event.target.id + "is clicked.");
- });
- })
- </script>
- off(events,[selector],[fn])
在选择元素上移除一个或多个事件的事件处理函数 off() 方法移除用. on()绑定的事件处理程序
移除上面 on 绑定的委托:
- [javascript] view plain copy
- print?
- $("#box1").off("click","p");
在网上看到了关于 事件委托的总结, 感觉挺不错的, 就翻译一下贴在这里, 跟大家分享一下, 如有错误或纰漏, 请指出 点击我查看原文;
总结:
先决条件是要有一个容器, 允许通用的事件处理函数
算法:
将事件处理函数绑定到容器上,
在事件处理函数内获取 event.target,
根据不同的 event.target 作相应的处理
应用场景:
需要为子元素用一个事件处理函数 处理相同的动作;
简化不同动作间的结构
优点:
简化了初始化的过程, 减少了多余的事件处理函数, 进而节省了内存
简化了 dom 节点更新时, 相应事件的更新
Allows to use innerHTML without additional processing.
缺点:
第一, 要求事件在 IE 中必须冒泡. 大多数的事件会冒泡, 但是并不是所有的对于其他的浏览器而言, 捕获阶段也会同样适用
第二, 理论上委托会导致浏览器额外的加载, 因为在容器内的任意一个地方事件的发生, 都会运行事件处理函数, 所以多数情况下事件处理函数都是在空循环(没有意义的动作), 通常不是什么大不了的事儿
来源: http://www.92to.com/bangong/2018/02-07/33290240.html