起因:

1、这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的;

2、其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考;

概述:

那什么叫事件委托呢?它还有一个名字叫事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。那这是什么意思呢?网上的各位大牛们讲事件委托基本上都用了同一个例子,就是取快递来解释这个现象,我仔细揣摩了一下,这个例子还真是恰当,我就不去想别的例子来解释了,借花献佛,我摘过来,大家认真领会一下事件委托到底是一个什么原理:

有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

这里其实还有2层意思的:

第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;

第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

为什么要用事件委托:

一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?

在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

事件委托的原理:

事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。

事件委托怎么实现:

终于到了本文的核心部分了,哈哈,在介绍事件委托的方法之前,我们先来看一段一般方法的例子:

子节点实现相同的功能:

  1. <ul id="ul1">
  2. <li>111</li>
  3. <li>222</li>
  4. <li>333</li>
  5. <li>444</li>
  6. </ul>

实现功能是点击li,弹出123:

  1. window.onload = function(){
  2. var oUl = document.getElementById("ul1");
  3. var aLi = oUl.getElementsByTagName('li');
  4. for(var i=0;i<aLi.length;i++){
  5. aLi[i].onclick = function(){
  6. alert(123);
  7. }
  8. }
  9. }

上面的代码的意思很简单,相信很多人都是这么实现的,我们看看有多少次的dom操作,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li;

那么我们用事件委托的方式做又会怎么样呢?

  1. window.onload = function(){
  2. var oUl = document.getElementById("ul1");
  3. oUl.onclick = function(){
  4. alert(123);
  5. }
  6. }

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

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

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(target.innerHTML);
    }
  }
}

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

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

  1. <div id="box">
  2. <input type="button" id="add" value="添加" />
  3. <input type="button" id="remove" value="删除" />
  4. <input type="button" id="move" value="移动" />
  5. <input type="button" id="select" value="选择" />
  6. </div>
  1. window.onload = function(){
  2. var Add = document.getElementById("add");
  3. var Remove = document.getElementById("remove");
  4. var Move = document.getElementById("move");
  5. var Select = document.getElementById("select");
  6.  
  7. Add.onclick = function(){
  8. alert('添加');
  9. };
  10. Remove.onclick = function(){
  11. alert('删除');
  12. };
  13. Move.onclick = function(){
  14. alert('移动');
  15. };
  16. Select.onclick = function(){
  17. alert('选择');
  18. }
  19.  
  20. }

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

  1. window.onload = function(){
  2. var oBox = document.getElementById("box");
  3. oBox.onclick = function (ev) {
  4. var ev = ev || window.event;
  5. var target = ev.target || ev.srcElement;
  6. if(target.nodeName.toLocaleLowerCase() == 'input'){
  7. switch(target.id){
  8. case 'add' :
  9. alert('添加');
  10. break;
  11. case 'remove' :
  12. alert('删除');
  13. break;
  14. case 'move' :
  15. alert('移动');
  16. break;
  17. case 'select' :
  18. alert('选择');
  19. break;
  20. }
  21. }
  22. }
  23.  
  24. }

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

现在讲的都是document加载完成的现有dom节点下的操作,那么如果是新增的节点,新增的节点会有事件吗?也就是说,一个新员工来了,他能收到快递吗?

看一下正常的添加节点的方法:

  1. <input type="button" name="" id="btn" value="添加" />
  2. <ul id="ul1">
  3. <li>111</li>
  4. <li>222</li>
  5. <li>333</li>
  6. <li>444</li>
  7. </ul>

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

  1. window.onload = function(){
  2. var oBtn = document.getElementById("btn");
  3. var oUl = document.getElementById("ul1");
  4. var aLi = oUl.getElementsByTagName('li');
  5. var num = 4;
  6.  
  7. //鼠标移入变红,移出变白
  8. for(var i=0; i<aLi.length;i++){
  9. aLi[i].onmouseover = function(){
  10. this.style.background = 'red';
  11. };
  12. aLi[i].onmouseout = function(){
  13. this.style.background = '#fff';
  14. }
  15. }
  16. //添加新节点
  17. oBtn.onclick = function(){
  18. num++;
  19. var oLi = document.createElement('li');
  20. oLi.innerHTML = 111*num;
  21. oUl.appendChild(oLi);
  22. };
  23. }

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

  1. window.onload = function(){
  2. var oBtn = document.getElementById("btn");
  3. var oUl = document.getElementById("ul1");
  4. var aLi = oUl.getElementsByTagName('li');
  5. var num = 4;
  6.  
  7. function mHover () {
  8. //鼠标移入变红,移出变白
  9. for(var i=0; i<aLi.length;i++){
  10. aLi[i].onmouseover = function(){
  11. this.style.background = 'red';
  12. };
  13. aLi[i].onmouseout = function(){
  14. this.style.background = '#fff';
  15. }
  16. }
  17. }
  18. mHover ();
  19. //添加新节点
  20. oBtn.onclick = function(){
  21. num++;
  22. var oLi = document.createElement('li');
  23. oLi.innerHTML = 111*num;
  24. oUl.appendChild(oLi);
  25. mHover ();
  26. };
  27. }

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

  1. window.onload = function(){
  2. var oBtn = document.getElementById("btn");
  3. var oUl = document.getElementById("ul1");
  4. var aLi = oUl.getElementsByTagName('li');
  5. var num = 4;
  6.  
  7. //事件委托,添加的子元素也有事件
  8. oUl.onmouseover = function(ev){
  9. var ev = ev || window.event;
  10. var target = ev.target || ev.srcElement;
  11. if(target.nodeName.toLowerCase() == 'li'){
  12. target.style.background = "red";
  13. }
  14.  
  15. };
  16. oUl.onmouseout = function(ev){
  17. var ev = ev || window.event;
  18. var target = ev.target || ev.srcElement;
  19. if(target.nodeName.toLowerCase() == 'li'){
  20. target.style.background = "#fff";
  21. }
  22.  
  23. };
  24.  
  25. //添加新节点
  26. oBtn.onclick = function(){
  27. num++;
  28. var oLi = document.createElement('li');
  29. oLi.innerHTML = 111*num;
  30. oUl.appendChild(oLi);
  31. };
  32. }

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

总结:

那什么样的事件可以用事件委托,什么样的事件不可以用呢?

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

好了,今天就到这里,下次我想介绍一下事件绑定,欢迎大家关注和阅读,以上纯属个人见解,如有不对的地方,万望指正,不胜感谢!

转自:http://www.cnblogs.com/liugang-vip/p/5616484.html

js_事件委托的更多相关文章

  1. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  2. js中的事件委托

    原文 http://itindex.net/detail/48661-js-事件 1,什么是事件委托:通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢 ...

  3. js动态添加事件-事件委托

    作者:白狼 出处:http://www.manks.top/javascript-dynamic-event.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给 ...

  4. 怎么理解js中的事件委托

    怎么理解js中的事件委托 时间 2015-01-15 00:59:59  SegmentFault 原文  http://segmentfault.com/blog/sunchengli/119000 ...

  5. jQuery-1.9.1源码分析系列(十) 事件系统——事件委托

    jQuery的事件绑定有几个比较优秀的特点: 1. 可以绑定不限数量的处理函数 2. 事件可以委托到祖先节点,不必一定要绑到对应的节点,这样后添加的节点也照样能被处理. 3. 链式操作 下面主要分析事 ...

  6. js事件委托

    什么是事件委托:通俗的讲,onclick,onmouseover,onmouseout,等就是事件,委托呢,就是让别人来做,这个事件本来是加在某些元素上的,然而你却加到别人身上来做,完成这个事件. 也 ...

  7. [JS]笔记14之事件委托

    -->什么是事件委托-->事件委托原理-->事件委托优点-->事件源 / 目标源 一.什么是事件委托 通俗的讲,onclick,onmouseover,onmouseout等这 ...

  8. 记一次事件委托在 ios 下的兼容 bug

    项目中碰到的兼容类 bug,记录一二. 页面上有几个同类型的控件,点击它们会触发一些事件,很显然,事件委托优于批量绑定.为了图方便,我将 click 事件绑定到了 document.body 上(绑定 ...

  9. C#-WebForm-WebForm开发基础、如何给控件注册事件?——事件委托写法、http无状态性、三层结构

    (小知识 - xml:可扩展的标记语言 html:超文本标记语言) 一.创建WebForm:新建→网站 此时文件夹中只有一个 config 文件,打开后 二.在项目下右键添加新项 在设计页面中打开 从 ...

随机推荐

  1. LOL one Key

    玩英雄联盟的小伙伴们快点来,犀利的工具哦,注意一点哦,放技能的时候别按空格,不然放不出来.最后说一句这几天常说的话 新年快乐.. 下载地址:http://pan.baidu.com/s/1eQzJ8l ...

  2. BZOJ 3489: A simple rmq problem

    3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 600 MBSubmit: 1594  Solved: 520[Submit] ...

  3. C# 扩展系统类方法

    1.声明扩展方法的步骤:类必须是static,方法是static 2.第一个参数是被扩展的对象,前面标注this 3.使用扩展方法的时候必须保证扩展方法类已经在当前代码中using 例子:using ...

  4. Django models 操作高级补充

    Django models 操作高级补充 字段参数补充: 外键 约束取消 ..... ORM中原生SQL写法: raw connection extra

  5. weiphp踩坑记录

    记录使用weiphp过程中遇到的问题及解决办法: 1.按照网上的教程创建插件怎么没有效果?replyText没有回复信息? 后台清除缓存即可!

  6. 推荐一款Mac上好用的Markdown编辑器

    [TOC] 推荐一款Mac上好用的Markdown编辑器 正文 我算是一位Evernote的重度用户吧.之前也尝试过为知笔记,有道云笔记,微软的onenote,最后还是觉的Evernote.因为我喜欢 ...

  7. SNMP Tutorial

    Applications: Internet Management (SNMP) 30.1 Introduction 30.2 The Level Of Management Protocols 30 ...

  8. python模块(shelve,xml,configparser,hashlib,logging)

    1.1shelve模块 shelve 模块比pickle模块简单,只有一个open函数,返回类似字典对象,可读可写:key必须为字符串, 而值可以是python所支持的数据类型. shelve模块主要 ...

  9. js实现继承的5种方式 (笔记)

    js实现继承的5种方式 以下 均为 ES5 的写法: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承 ...

  10. 的vim和emacs常用配置记录

    因为经常改变工作环境,所以在这里记录一下我的.vimrc文件和.emacs文件的内容. vimrc的配置文件,主要是设置语法高亮,和tab键的宽度,个人喜欢设置为4个空格的分量. " set ...