• js实例分析JavaScript中的事件委托和事件绑定


     我们在学习JavaScript中,难免都会去网上查一些资料。也许偶尔就会遇到“事件委托”(也有的称我“事件代理”,这里不评论谁是谁非。以下全部称为“事件委托”),尤其是在查JavaScript的事件处理的时候。但是,大多数时说的是“事件绑定”,对于“事件委托”,或是不提,或是浅尝辄止。对于我这个比较好奇的人来说,实在很蛋疼。尤其是想更多的了解“事件委托”的时候。

      这次干脆一劳永逸,自己把查出来的资料整理成一篇日志,总结这块的知识,也方便需要的朋友查阅。

    JavaScript中事件传播过程那些事儿

      早期的web开发,浏览器厂商很难回答一个哲学上的问题:当你在页面上的一个区域点击时,你真正感兴趣的是哪个元素。这个问题带来了交互的定义。在一个元素的界限内点击,显得有点含糊。毕竟,在一个元素上的点击同时也发生在另一个元素的界限内。例如单击一个按钮。你实际上点击了按钮区域、body元素的区域以及html元素的区域。

      伴随着这个问题,两种主流的浏览器Netscape和IE有不同的解决方案。Netscape定义了一种叫做事件捕获的处理方法,事件首先发生在DOM树的最高层对象(document)然后往最深层的元素传播。在图例中,事件捕获首先发生在document上,然后是html元素,body元素,最后是button元素。

      IE的处理方法正好相反。他们定义了一种叫事件冒泡的方法。事件冒泡认为事件促发的最深层元素首先接收事件。然后是它的父元素,依次向上,知道document对象最终接收到事件。尽管相对于html元素来说,document没有独立的视觉表现,他仍然是html元素的父元素并且事件能冒泡到document元素。所以图例中噢噢那个button元素先接收事件,然后是body、html最后是document。如下图:

    JavaScript事件冒泡和事件捕捉

    图1-JavaScript时间冒泡和捕捉

     

     

    事件冒泡:

      什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层。这时,从最底层冒出了一个气泡,气泡会一层一层地上升,直到最顶层。而你不管在水的哪一层观察都可以看到并捕捉到这个气泡。好了,把“水”改成“DOM”,把“气泡”改成“事件”。这就是“事件冒泡”。

      气泡带上了某种信息,会告诉其经过的每一层自己是在哪一层产生的。JavaScript的事件确实会带着这个属性。当程序捕获一个事件的时候,它会知道这个事件来自于页面上哪个元素。 事件委托也就是利用这个原理。

    事件委托和事件绑定的简介

      事件绑定的介绍,网上比比皆是,大家也比较熟悉,D瓜哥文笔不好,就不献丑了。

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

    事件委托和事件绑定的代码实现

      事件绑定的代码,网上一抓一把。这里只给出一个简单代码。诸如:

    1 document.getElementById("id").onclick=function(){
    2    //这里是事件处理代码
    3 }

      D瓜哥在第一次注意到事件委托的时候,明白这个大概意思。不过,陆游他老人家有句话说的好,“纸上得来终觉浅,绝知此事要躬行。”英语有一个很“心领神会”的一句话,是世界著名计算机专家Donald Knuth说的,”An algorithm must be seen to be believe!“D瓜哥从第一次注意到“事件委托”后,就考虑用代码如何实现。相信,这也是很多人第一次注意到“事件委托”的想法吧。哈哈

      这里给大家一个简要的示例。示例如下:

    01 //document.onclick,从这点就能看出,这个示例把事件委托放到了document上。
    02 document.onclick = function(event){
    03     //IE doesn't pass in the event object
    04     event = event || window.event;
    05      
    06     //IE uses srcElement as the target
    07     var target = event.target || event.srcElement;
    08      
    09     switch(target.id){
    10         case "help-btn":
    11                 openHelp();
    12                 break;
    13         case "save-btn":
    14                 saveDocument();
    15                 break;
    16         case "undo-btn":
    17                 undoChanges();
    18                 break;
    19         //如果有其元素需要处理点击事件,
    20         //只需要在这里添加不同的case分支就行。
    21     }
    22 };

      当然,这里代码为了说明”事件委托”,代码尽可能简化了。不能用于工业生产。另外,为了写例子的方便和内容需要,一下的代码使用jQuery框架来实现相关代码。

      我们了解过了如何使用代码来实现“事件委托”和“事件绑定”了。下面我们从“处理速度”,“新增元素的处理”和“内存消耗”三方面来论述一下这两者的区分。

    事件委托和事件绑定的处理速度对比

      先上段代码,让大家体验一下两种事件处理在速度上的巨大差别。业务逻辑很简单:点击按钮,先生成对应数量的元素,然后再分别利用“事件绑定”和“事件委托”各个生成的元素增加事件处理。

      通过上面的测试,可以看出,在元素数量比较大的时候,“事件委托”的效率灰常明显的比“事件绑定”好好的太多太多了(随着元素数量的增加,两者对比实在不成比例,用数量级差别以不足以形容了。哈哈)。在“高性能JavaScript”中,我提出来一些提高JavaSctipt性能的准则。“尽量使用‘事件委托’进行事件处理”也应该做为一条高性能JavaScript准则,加入进去。

    事件委托和事件绑定针对新增元素的事件处理的对比

      在一些页面中,难免有时会增加一些新的“元素”。针对这些新增加的元素,事件绑定使如何处理的呢?事件委托又会如何呢?下面,我们还是使用实例来说话。代码如下:

      大家可以点击“点击新增元素”,来新增元素,然后再点击新增出来的元素,看看效果如何?

      大家也看到了,对于“事件绑定”中的新元素,并没有添加事件处理;但是,对于“事件委托”中的新元素,每个新元素都有事件处理。这是为啥相信大家都很明白?这是因为“事件绑定”是针对每个元素进行事件绑定处理,但是新的元素并没有进行事件绑定处理;而“事件委托”,是针对某个选择器下的某种元素,都进行事件处理。所以,只需要这些元素符合这个条件,都会进行事件处理。

    事件委托和事件绑定的占用内存对比

      如果一个整体页面里有大量的需要绑定事件的元素,每个元素都要绑定一个函数,而每个函数都是对象,对象就会占用很多内存,内存中的对象越多,性能就越差。

      而事件委托,只在绑定事件的上级元素上绑定函数,次数十分有限(一般每次委托只有一次);另外,在事件被触发时,才会取出触发事件的元素来进一步处理。总体来讲,非常节约内存。

      我们来用实际例子来对比一下。还是利用上面速度测试中的例子,URL如下:
    事件绑内存消耗测试URL:http://jsfiddle.net/DiGuaGe/MbYy6/2/embedded/result/
    事件委托内存消耗测试URL:http://jsfiddle.net/DiGuaGe/6d7J6/1/embedded/result/
    创建元素内存消耗测试URL:http://jsfiddle.net/DiGuaGe/UCXbM/embedded/result/
    用IE浏览器打开,然后再打开“Windows任务管理器”,选中“进程”Tab页,注意观察“iexplore.exe”(这个就是IE浏览器的进程),可以看出内存消耗。

      在我的笔记本上(硬件,Dell E6410;Core i5;2G内存;软件:IE8)测试结果差距灰常大。具体对比如下:

    JavaScript事件委托和事件绑定的内存消耗对比

    图2-JavaScript事件委托和事件绑定的内存消耗对比”

     

      D瓜哥从“处理速度”、“新增元素事件处理”和“内存消耗”三方面比较了“事件委托”和“事件绑定”的对比,可以很容易看出,“事件委托”在“处理速度”和“内存消耗”上,有得天独厚的优势。所以,在Web编程的时候,尤其在构建大型系统的时候,应该尽量考虑使用“事件委托”。但是,“事件委托”并不是万能的;它也有一些弊端。下面我们在论述一下它的弊端。

    使用“事件委托”时,需要注意的问题

      使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。

      这点也可以从jQuery .live()方法的数次升级中看出一些“端倪”。

      jQuery 1.3新增了.live()方法来进行事件处理。刚开始时 .live()方法会把click事件绑定到$(document)对象。代码如下:

    1 $("#info_table td").live("click",function(){/*事件处理*/});

      默认把事件绑定到$(document)元素,如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。

      为了避免事件冒泡造成的性能损失,jQuery从1.4开始支持在使用.live()方法时配合使用一个上下文参数。代码如下:

    1 $("td",$("#info_table")[0]).live("click",function(){/*事件处理*/});

      这样,”受托方”就从默认的$(document)变成了$(“#infotable”)[0],节省了冒泡的旅程。不过,与.live()共同使用的上下文参数必须是一个单独的DOM元素,所以这里指定上下文对象时使用的是$(“#infotable”)[0],即使用数组的索引操作符来取得的一个DOM元素。

      再说说一点。为了解决无谓生成元素集合的问题,jQuery 1.4.2干脆直接引入了一个新方法.delegate()。使用.delegate(),前面的例子可以这样写:

    1 $("#info_table td").live("click",function(){/*事件处理*/});

      《重构与模式》中有一句非常经典的话:“如果想成为一名更优秀的软件设计师,了解优秀软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中隐藏着大智慧。”看来jQuery的live方法的演变,感觉用这句话说这个演变过程是如此的贴切。所以,我们在学习中,也许从一门技术的演变中,得到灵感,创造出更好的技术!

      这篇文章,参考了很多资料。这里表示感谢。另外,大家如果有什么好的见解,欢迎留言进行讨论。

    注:

      关于更多的jQuery的介绍,请阅读参考资料中《jQuery代码优化:事件委托篇》。

    参考资料:

    1. jQuery代码优化:事件委托篇
    2. JavaScript中的事件委托
    3. JavaScript事件冒泡和事件委托
    4. javascript 事件委托
    5. JavaScript事件委托优化
    6. JavaScript跨浏览器的添加删除事件绑定函数


    作 者: D瓜哥,http://www.diguage.com/
    原文链接:http://www.diguage.com/archives/71.html
    版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
  • 相关阅读:
    js监听手机端点击物理返回键或js监听pc端点击浏览器返回键
    mysql存储emoji问题
    windows环境下 php 将office文件(word/excel/ppt)转化为pdf
    javascript 获取多种主流浏览器显示页面高度
    iframe 加载外部资源,显示隐藏loading,onload失效
    ubuntu 忽略文件的50unattended升级问题
    ubuntu apt 软件源的更改
    Python3.6连接mysql(一)
    H5图片预览、压缩、上传
    前端如何上传图片到七牛云
  • 原文地址:https://www.cnblogs.com/daysme/p/6656464.html
Copyright © 2020-2023  润新知