热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JS性能优化之事件委托

面试中2次被问到过这个知识点,实际开发中,应用事件委托也比较常见。JS中事件委托的实现主要依赖于事件冒泡。那什么是事件冒泡?就是事件从最深

面试中2次被问到过这个知识点,实际开发中,应用事件委托也比较常见。JS中事件委托的实现主要依赖于 事件冒泡 。那什么是事件冒泡?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。

 

为什么要用事件委托呢?一是为了图方便,二是为了提升性能。具体我们来举个例子看下:

HTML结构代码

<ul id&#61;"box"><li>demoli><li>demoli><li>demoli><li>demoli>...<li>demoli><li>demoli><li>demoli>
ul>

 

我们现在要给 ul 下的 li 添加点击事件。如果你不知道或不会运用事件委托&#xff0c;你的代码可能是下面这样的&#xff1a;

var box &#61; document.getElementById(&#39;box&#39;),lis &#61; box.getElementsByTagName(&#39;li&#39;);for (var i &#61; lis.length - 1; i >&#61; 0; i--) {lis[i].onclick &#61; function() {}
}

上面代码很简单&#xff0c;逻辑清晰。我们看看有多少次的dom操作&#xff0c;首先要找到ul&#xff0c;然后遍历li&#xff0c;然后点击li的时候&#xff0c;又要找一次目标的li的位置&#xff0c;才能执行最后的操作&#xff0c;每次点击都要找一次li。我们再来看下应用 事件委托 后的代码&#xff1a;

var box &#61; document.getElementById(&#39;box&#39;);
box.onclick
&#61; function() { }

这里用父级ul做事件处理&#xff0c;当li被点击时&#xff0c;由于冒泡原理&#xff0c;事件就会冒泡到ul上&#xff0c;因为ul上有点击事件&#xff0c;所以事件就会触发&#xff0c;当然&#xff0c;这里当点击ul的时候&#xff0c;也是会触发的&#xff0c;那么问题就来了&#xff0c;如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办&#xff0c;比如说只有点击li才会触发&#xff1f;

Event对象提供了一个属性叫target&#xff0c;可以返回事件的目标节点&#xff0c;我们成为事件源&#xff0c;也就是说&#xff0c;target就可以表示为当前的事件操作的dom&#xff0c;但是不是真正操作dom&#xff0c;当然&#xff0c;这个是有兼容性的&#xff0c;标准浏览器用ev.target&#xff0c;IE浏览器用event.srcElement&#xff0c;此时只是获取了当前节点的位置&#xff0c;并不知道是什么节点名称&#xff0c;这里我们用nodeName来获取具体是什么标签名&#xff0c;这个返回的是一个大写的&#xff0c;我们需要转成小写再做比较&#xff08;习惯问题&#xff09;&#xff1a;

var box &#61; document.getElementById("box");
box.onclick
&#61; function(ev) {var ev &#61; ev || window.event;var target &#61; ev.target || ev.srcElement;if(target.nodeName.toLowerCase() &#61;&#61;&#61; &#39;li&#39;){}
}

这样改下就只有点击li会触发事件了&#xff0c;且每次只执行一次dom操作&#xff0c;如果li数量很多的话&#xff0c;将大大减少dom的操作&#xff0c;优化的性能可想而知&#xff01;

 

上面的例子是说li操作的是同样的效果&#xff0c;要是每个li被点击的效果都不一样&#xff0c;那么用事件委托还有用吗&#xff1f;

<div id&#61;"box"><input type&#61;"button" id&#61;"add" value&#61;"添加" /><input type&#61;"button" id&#61;"remove" value&#61;"删除" /><input type&#61;"button" id&#61;"move" value&#61;"移动" /><input type&#61;"button" id&#61;"select" value&#61;"选择" />
div>

常规思路代码&#xff1a;

var Add &#61; document.getElementById("add");
var Remove &#61; document.getElementById("remove");
var Move &#61; document.getElementById("move");
var Select &#61; document.getElementById("select");Add.onclick &#61; function(){alert(&#39;添加&#39;);
};
Remove.onclick
&#61; function(){alert(&#39;删除&#39;);
};
Move.onclick
&#61; function(){alert(&#39;移动&#39;);
};
Select.onclick
&#61; function(){alert(&#39;选择&#39;);
}

 

上面实现的效果我就不多说了&#xff0c;很简单&#xff0c;4个按钮&#xff0c;点击每一个做不同的操作&#xff0c;那么至少需要4次dom操作&#xff0c;如果用事件委托&#xff0c;能进行优化吗&#xff1f;

var oBox &#61; document.getElementById("box");
oBox.onclick
&#61; function (ev) {var ev &#61; ev || window.event;var target &#61; ev.target || ev.srcElement;if(target.nodeName.toLocaleLowerCase() &#61;&#61; &#39;input&#39;){switch(target.id){case &#39;add&#39; :alert(&#39;添加&#39;);break;case &#39;remove&#39; :alert(&#39;删除&#39;);break;case &#39;move&#39; :alert(&#39;移动&#39;);break;case &#39;select&#39; :alert(&#39;选择&#39;);break;}}
}

用事件委托就可以只用一次dom操作就能完成所有的效果&#xff0c;比上面的性能肯定是要好一些的 。

 

现在讲的都是document加载完成的现有dom节点下的操作&#xff0c;那么如果是新增的节点&#xff0c;新增的节点会有事件吗&#xff1f;也就是说&#xff0c;一个新员工来了&#xff0c;他能收到快递吗&#xff1f;看一下正常的添加节点的方法&#xff1a;

<input type&#61;"button" name&#61;"" id&#61;"btn" value&#61;"添加" />
<ul id&#61;"ul1"><li>111li><li>222li><li>333li><li>444li>
ul>

现在是移入li&#xff0c;li变红&#xff0c;移出li&#xff0c;li变白&#xff0c;这么一个效果&#xff0c;然后点击按钮&#xff0c;可以向ul中添加一个li子节点

var oBtn &#61; document.getElementById("btn");
var oUl &#61; document.getElementById("ul1");
var aLi &#61; oUl.getElementsByTagName(&#39;li&#39;);
var num &#61; 4;//鼠标移入变红&#xff0c;移出变白
for(var i&#61;0; i){aLi[i].onmouseover &#61; function(){this.style.background &#61; &#39;red&#39;;};aLi[i].onmouseout &#61; function(){this.style.background &#61; &#39;#fff&#39;;}}//添加新节点oBtn.onclick &#61; function(){num&#43;&#43;;var oLi &#61; document.createElement(&#39;li&#39;);oLi.innerHTML &#61; 111*num;oUl.appendChild(oLi);};

 

 这是一般的做法&#xff0c;但是你会发现&#xff0c;新增的li是没有事件的&#xff0c;说明添加子节点的时候&#xff0c;事件没有一起添加进去&#xff0c;这不是我们想要的结果&#xff0c;那怎么做呢&#xff1f;一般的解决方案会是这样&#xff0c;将for循环用一个函数包起来&#xff0c;命名为mHover&#xff0c;如下&#xff1a;

var oBtn &#61; document.getElementById("btn");
var oUl &#61; document.getElementById("ul1");
var aLi &#61; oUl.getElementsByTagName(&#39;li&#39;);
var num &#61; 4;function mHover () {//鼠标移入变红&#xff0c;移出变白for(var i&#61;0; i){aLi[i].onmouseover &#61; function(){this.style.background &#61; &#39;red&#39;;};aLi[i].onmouseout &#61; function(){this.style.background &#61; &#39;#fff&#39;;}}
}
mHover ();
//添加新节点
oBtn.onclick &#61; function(){num&#43;&#43;;var oLi &#61; document.createElement(&#39;li&#39;);oLi.innerHTML &#61; 111*num;oUl.appendChild(oLi);mHover ();
};

 

 虽然功能实现了&#xff0c;看着还挺好&#xff0c;但实际上无疑是又增加了一个dom操作&#xff0c;在优化性能方面是不可取的&#xff0c;那么有事件委托的方式&#xff0c;能做到优化吗&#xff1f;

var oBtn &#61; document.getElementById("btn");
var oUl &#61; document.getElementById("ul1");
var aLi &#61; oUl.getElementsByTagName(&#39;li&#39;);
var num &#61; 4;//事件委托&#xff0c;添加的子元素也有事件
oUl.onmouseover &#61; function(ev){var ev &#61; ev || window.event;var target &#61; ev.target || ev.srcElement;if(target.nodeName.toLowerCase() &#61;&#61; &#39;li&#39;){target.style.background &#61; "red";}};
oUl.onmouseout
&#61; function(ev){var ev &#61; ev || window.event;var target &#61; ev.target || ev.srcElement;if(target.nodeName.toLowerCase() &#61;&#61; &#39;li&#39;){target.style.background &#61; "#fff";}};//添加新节点oBtn.onclick &#61; function(){num&#43;&#43;;var oLi &#61; document.createElement(&#39;li&#39;);oLi.innerHTML &#61; 111*num;oUl.appendChild(oLi);};

 

 看&#xff0c;上面是用事件委托的方式&#xff0c;新添加的子元素是带有事件效果的&#xff0c;我们可以发现&#xff0c;当用事件委托的时候&#xff0c;根本就不需要去遍历元素的子节点&#xff0c;只需要给父级元素添加事件就好了&#xff0c;其他的都是在js里面的执行&#xff0c;这样可以大大的减少dom操作&#xff0c;这才是事件委托的精髓所在。

 

现在给一个场景 ul > li > div > p&#xff0c;div占满li&#xff0c;p占满div&#xff0c;还是给ul绑定时间&#xff0c;需要判断点击的是不是li&#xff08;假设li里面的结构是不固定的&#xff09;&#xff0c;那么e.target就可能是p&#xff0c;也有可能是div&#xff0c;这种情况你会怎么处理呢&#xff1f;

那我们现在就再现一下这个场景&#xff1a;

<ul id&#61;"test"><li><p>11111111111p>li><li><div>22222222div>li><li><span>3333333333span>li><li>4444444li>
ul>

 

如上列表&#xff0c;有4个li&#xff0c;里面的内容各不相同&#xff0c;点击li&#xff0c;event对象肯定是当前点击的对象&#xff0c;怎么指定到li上&#xff0c;下面我直接给解决方案&#xff1a;

var oUl &#61; document.getElementById(&#39;test&#39;);
oUl.addEventListener(
&#39;click&#39;,function(ev){var target &#61; ev.target;while(target !&#61;&#61; oUl ){if(target.tagName.toLowerCase() &#61;&#61; &#39;li&#39;){console.log(&#39;li click~&#39;);break;}target &#61; target.parentNode;}
})

 

核心代码是while循环部分&#xff0c;实际上就是一个递归调用&#xff0c;你也可以写成一个函数&#xff0c;用递归的方法来调用&#xff0c;同时用到冒泡的原理&#xff0c;从里往外冒泡&#xff0c;知道currentTarget为止&#xff0c;当当前的target是li的时候&#xff0c;就可以执行对应的事件了&#xff0c;然后终止循环&#xff0c;恩&#xff0c;没毛病&#xff01;这里看不到效果&#xff0c;大家可以复制过去运行一下&#xff01;

 

转载自&#xff1a; https://www.cnblogs.com/liugang-vip/p/5616484.html

转:https://www.cnblogs.com/similar/p/8502660.html



推荐阅读
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 本文介绍了一种使用 JavaScript 计算两个日期之间时间差的方法。该方法支持多种时间格式,并能返回秒、分钟、小时和天数等不同精度的时间差。 ... [详细]
  • 利用REM实现移动端布局的高效适配技巧
    在移动设备上实现高效布局适配时,使用rem单位已成为一种流行且有效的技术。本文将分享过去一年中使用rem进行布局适配的经验和心得。rem作为一种相对单位,能够根据根元素的字体大小动态调整,从而确保不同屏幕尺寸下的布局一致性。通过合理设置根元素的字体大小,开发者可以轻松实现响应式设计,提高用户体验。此外,文章还将探讨一些常见的问题和解决方案,帮助开发者更好地掌握这一技术。 ... [详细]
  • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 全面解析JavaScript代码注释技巧与标准规范
    在Web前端开发中,JavaScript代码的可读性和维护性至关重要。本文将详细介绍如何有效地使用注释来提高代码的可读性,并探讨JavaScript代码注释的最佳实践和标准规范。通过合理的注释,开发者可以更好地理解和维护复杂的代码逻辑,提升团队协作效率。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • 如何撰写PHP电商项目的实战经验? ... [详细]
  • 本文最初发表在Thorben Janssen的Java EE博客上,每周都会分享最新的Java新闻和动态。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本项目通过Python编程实现了一个简单的汇率转换器v1.02。主要内容包括:1. Python的基本语法元素:(1)缩进:用于表示代码的层次结构,是Python中定义程序框架的唯一方式;(2)注释:提供开发者说明信息,不参与实际运行,通常每个代码块添加一个注释;(3)常量和变量:用于存储和操作数据,是程序执行过程中的重要组成部分。此外,项目还涉及了函数定义、用户输入处理和异常捕获等高级特性,以确保程序的健壮性和易用性。 ... [详细]
  • Webdriver中元素定位的多种技术与策略
    在Webdriver中,元素定位是自动化测试的关键环节。本文详细介绍了8种常用的元素定位技术与策略,包括ID、名称、标签名、类名、链接文本、部分链接文本、XPath和CSS选择器。每种方法都有其独特的优势和适用场景,通过合理选择和组合使用,可以显著提高测试脚本的稳定性和效率。此外,文章还探讨了在复杂页面结构中如何灵活运用这些定位技术,以应对各种挑战。 ... [详细]
  • 系统转换的三种方法及其具体应用分析
    系统转换是信息技术领域中常见的任务,本文详细探讨了三种主要的系统转换方法及其具体应用场景。这些方法包括:代码迁移、数据迁移和平台迁移。文章通过实例分析了每种方法的优势和局限性,并提供了实际操作中的注意事项和技术要点。例如,代码迁移适用于从VB6获取网页源码,数据迁移在Ubuntu中用于隐藏侧边栏,而平台迁移则涉及Tomcat 6.0的使用和谷歌爬虫的测试。此外,文章还讨论了蓝翰互动PHP面试和5118 SEO工具在系统转换中的应用,为读者提供了全面的技术参考。 ... [详细]
  • 织梦系统多条件联动筛选功能详细教程及删除操作指南
    多条件联动筛选功能广泛应用于图片展示、装修设计、机械设备和在线商城等场景,通常筛选条件应聚焦于用户最关心的要素,而非涵盖所有可能的选项。在DedeCMS中,多条件筛选的PHP开发并未内置删除已选条件的功能,但通过理解PHP筛选与JS筛选的不同机制,实现这一功能相对简单且易于操作。 ... [详细]
  • 期末Web开发综合实践项目:运用前端技术打造趣味小游戏体验
    期末Web开发综合实践项目中,学生通过运用HTML、CSS和JavaScript等前端技术,设计并实现了一款趣味性十足的小游戏。该项目不仅检验了学生对前端基础知识的掌握情况,还提升了他们的实际操作能力和创意设计水平。视频链接展示了项目的最终成果,直观呈现了游戏的互动性和视觉效果。 ... [详细]
author-avatar
dujiaolianglong
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有