热门标签 | 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代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路
    本文介绍了FineReport平台数据分析图表显示部分系列接口的应用场景和实现思路。当图表系列较多时,用户希望可以自己设置哪些系列显示,哪些系列不显示。通过调用FR.Chart.WebUtils.getChart("chartID").getChartWithIndex(chartIndex).setSeriesVisible()接口,可以获取需要显示的系列图表对象,并在表单中显示这些系列。本文以决策报表为例,详细介绍了实现方法,并给出了示例。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • 深入理解CSS中的margin属性及其应用场景
    本文主要介绍了CSS中的margin属性及其应用场景,包括垂直外边距合并、padding的使用时机、行内替换元素与费替换元素的区别、margin的基线、盒子的物理大小、显示大小、逻辑大小等知识点。通过深入理解这些概念,读者可以更好地掌握margin的用法和原理。同时,文中提供了一些相关的文档和规范供读者参考。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
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社区 版权所有