热门标签 | 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



推荐阅读
  • JavaScript 实现购物商城商品图片放大功能
    本文介绍了如何使用 JavaScript 和 CSS 实现购物商城中商品图片的放大功能,解决了图片放大时文字位置变化的问题,并提供了详细的代码示例。 ... [详细]
  • 本文针对公司项目中普遍存在的IE浏览器兼容性问题,特别是IE9及以下版本,提出了具体的解决方案,确保用户在这些旧版浏览器中也能顺利实现图片上传预览功能。 ... [详细]
  • 本文详细介绍了如何手动编写兼容IE的Ajax函数,以及探讨了跨域请求的实现方法和原理,包括JSONP和服务器端设置HTTP头部等技术。 ... [详细]
  • 本文将详细介绍如何使用ViewPager实现多页面滑动切换,并探讨如何去掉其默认的左右切换动画效果。ViewPager是Android开发中常用的组件之一,用于实现屏幕间的内容切换。 ... [详细]
  • HTML与JS结合实现点击展开全文功能
    在网页设计中,为了优化用户体验,常常会在长篇文章中设置“点击展开全文”按钮,以减少初始加载时间并提高页面加载速度。本文将详细介绍如何使用HTML和JavaScript轻松实现这一功能。 ... [详细]
  • 本文介绍了Windows驱动开发的基础知识,包括WDF(Windows Driver Framework)和WDK(Windows Driver Kit)的概念及其重要特性,旨在帮助开发者更好地理解和利用这些工具来简化驱动开发过程。 ... [详细]
  • 首先说一下,这是我在CSDN上的第一个文章,其实这个账号早在几年前就申请了,不过当时只是为了下载一个资源,而且也不怎么懂信息技术相关的领域,后来就再也没怎么动过,直到今天我才开始使用这个账号 ... [详细]
  • 本文探讨了如何利用自定义URI方案和注册表编辑,在Windows操作系统中实现从Web浏览器启动本地应用程序的方法,同时强调了这一过程中的安全考虑。 ... [详细]
  • 本文详细介绍了如何在VMware环境下安装CentOS 7 Minimal,并成功配置GNOME桌面环境的过程。包括解决网络连接问题和设置默认图形界面等关键步骤。 ... [详细]
  • 使用Python轻松合并大量复杂Excel文件
    当面对大量的Excel文件时,如何高效地将它们合并成一个文件成为了一项挑战。本文将指导初学者如何利用Python的几个库,在几十行代码内完成这一任务。 ... [详细]
  • 手把手教你构建简易JSON解析器
    本文将带你深入了解JSON解析器的构建过程,通过实践掌握JSON解析的基本原理。适合所有对数据解析感兴趣的开发者。 ... [详细]
  • 本文介绍如何利用JavaScript在页面加载时为ASP.NET的DropDownList控件设置特定的选项值。 ... [详细]
  • 探讨了一个关于Windows C++开发中遇到的乱码问题,特别是在处理宽字符时出现的情况。本文通过一个具体的示例——一个简单的窗口应用程序,展示了如何正确地使用宽字符以避免乱码。 ... [详细]
  • 前端监控系列2 | 深入探讨JS错误监控的重要性与实践
    作者:彭莉,火山引擎APM研发工程师,专注于前端监控技术的研发。本文将深入讨论JS错误监控的必要性及其实现方法,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了如何在不同域名下,通过浏览器直接下载PDF文件而非预览的问题,并提供了两种解决方案:一是利用原生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社区 版权所有