鱼C论坛

 找回密码
 立即注册
查看: 2101|回复: 10

[庖丁解牛] 0 2 3 6 ★ 原生JavaScript的事件委托

[复制链接]
发表于 2019-1-4 16:23:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 不二如是 于 2019-1-11 17:32 编辑




                               
登录/注册后可看大图


很多鱼油关于JS的问题会涉及到事件委托,在0 2 3 2 ★ JS执行机制大作战 中也涉及到了。

还是有必要仔细围绕这个东东,好好说一下。

事件委托的定义:
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。


何为事件冒泡(event bubbling)呢?

这个概念是有微软提出滴,事件冒泡可以形象地比喻为:
游客,如果您要查看本帖隐藏内容请回复


giphy.gif


也就是说:
事件会从最内层的元素开始发生,一直向上传播,直到document对象。


因此在事件冒泡的概念下在 p 元素上发生 click 事件的顺序应该是:
  1. p -> div -> body -> html -> document
复制代码


大概了解到这一层面就够了,有机会关于“事件捕获和冒泡”,好好讲一下~

继续回到事件委托,在 js 中添加到页面上的事件处理程序的个数直接影响到网页的运行性能。

因为每个事件处理函数都是一个对象,是对象就会占用内存,而内存中对象越多,导致的结果就是性能越差。

而访问 dom 的次数越多,就会引起结构的重绘或者重排的次数也随之增多,会延迟整个页面的交互就绪时间。

这样对于在页面进行处理过后新增的 dom 元素,运用事件委托可以为新增的dom元素一并增减事件处理程序。

理论就是上面这样啦,进入敲代码环节。

我们实现一个p元素单击时,背景变绿的交互,创建基本代码:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>事件委托</title>
  6. </head>
  7. <body>
  8. <div id="nodes">
  9.     <P class="node_p">第一个p</P>
  10.     <P class="node_p">第二个p</P>
  11.     <P class="node_p">第三个p</P>
  12.     <div id="childDiv">
  13.         <p>这是子集菜单</p>
  14.         <div>我是子集的div
  15.             <p>我是子集的p</p>
  16.         </div>
  17.     </div>
  18. </div>
  19. <button>单击新增一个p元素</button>
  20. </body>
  21. </html>
复制代码


接下来实现 JS 代码部分,我们先不要用事件委托,实现:
获取页面中所有的 p 元素然后用 for 循环遍历给每一个元素增加事件处理函数


创建JS:
  1. <script>
  2.     var nodes = document.getElementById("nodes");
  3.     var ps = document.getElementsByTagName("p");
  4.     console.log(ps);
  5.     var btn = document.getElementsByTagName("button")[0];
  6.     var inner = 33;
  7.     btn.onclick = function() {
  8.         inner++;
  9.         var p = document.createElement("p");
  10.         p.innerHTML = inner + "新增的p标签啊";
  11.         nodes.appendChild(p);
  12.         console.log(ps);
  13.     };
  14.     for (var i= 0;i<ps.length;i++){
  15.         ps[i].onclick= function(){
  16.             this.style.background = 'green'
  17.         }
  18.     }
  19. </script>
复制代码

Jan-11-2019 16-07-57.gif


然后单击p元素会看到:
Jan-11-2019 16-12-33.gif


原来存在的 p 元素单击后背景色正确变绿了,而新增加的p元素没有成功!

怎么肥四?

可以理解为浏览器的一种优化,如果每一个未经授权的新增元素获得所有现存的方法岂不乱套?

需要为新增的元素增加一个事件处理函数。

新增一个 myAddEvent() :
  1. var nodes = document.getElementById("nodes");
  2.     var ps = document.getElementsByTagName("p");
  3.     console.log(ps);
  4.     var btn = document.getElementsByTagName("button")[0];
  5.     var inner = 33;
  6.     btn.onclick = function() {
  7.         inner++;
  8.         var p = document.createElement("p");
  9.         p.innerHTML = inner + "新增的p标签啊";
  10.         nodes.appendChild(p);
  11.         // 将新dom元素增加到页面后在执行
  12.         myAddEvent();
  13.         console.log(ps);
  14.     };

  15.     // 增加事件处理函数,包裹for循环
  16.     function myAddEvent(){
  17.         for (var i= 0;i<ps.length;i++){
  18.         ps[i].onclick= function(){
  19.             this.style.background = 'green'
  20.         }
  21.     }
  22.     }
复制代码

Jan-11-2019 16-55-25.gif


搞定啦!

这时候虽然解决了为新增dom元素增加事件处理函数的问题,但是仔细考虑挥会发现:
性能却是比之前都有所下降。


究其原因就是又增加了一个事件处理函数(对象),又一次占用了内存。

所以,这个时候就会用到事件委托,这时候事件委托的优势也有所体现出来:
  1. var nodes = document.getElementById("nodes");
  2.     var ps = document.getElementsByTagName("p");
  3.     console.log(ps);
  4.     var btn = document.getElementsByTagName("button")[0];
  5.     var inner = 33;
  6.     btn.onclick = function() {
  7.         inner++;
  8.         var p = document.createElement("p");
  9.         p.innerHTML = inner + "新增的p标签啊";
  10.         nodes.appendChild(p);
  11.         console.log(ps);
  12.     };

  13.     // 事件委托
  14.     nodes.onclick = function(e){
  15.         var ev = e || window.event;
  16.         var target = ev.target;
  17.         //这里要判被处理元素节点的名字
  18.         if(target.nodeName.toLowerCase() == 'p'){
  19.             target.style.background = 'green'
  20.         }
  21.     }
复制代码

Jan-11-2019 17-05-32.gif


通过 if 我们判被处理元素节点的名字。

也可以增加相应的判断条件:
  1. target.nodeName.toLowerCase() == 'p'||target.nodeName.toLowerCase() == 'span'
复制代码


但是要注意不要使用父级元素的名称,因为再点击子元素之间的空气的时候,由于事件冒泡他会给父级元素也增加相应的事件处理函数。

因为返回的节点名称一般都是大写,所以这时要用 toLowerCase() 处理一下。


                               
登录/注册后可看大图








如果喜欢,别忘了评分


                               
登录/注册后可看大图


这位鱼油,如果喜欢本系列Js帖子,请订阅 专辑&#9758;传送门)(不喜欢更要订阅

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-1-11 11:11:39 | 显示全部楼层
终于更新了

点评

我很赞同!: 5.0
我很赞同!: 5
  发表于 2019-1-11 15:56

评分

参与人数 1鱼币 +6 收起 理由
不二如是 + 6 鱼C有你更精彩^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-3-10 20:12:10 | 显示全部楼层
谢谢楼主
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-11-5 23:07:15 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-5-11 14:33:24 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-11-21 14:48:57 | 显示全部楼层
kk
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-6-13 11:06:18 | 显示全部楼层
。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-9-26 08:57:01 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-21 14:57:18 | 显示全部楼层
1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-10-14 14:23:05 | 显示全部楼层
1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-25 09:30

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表