引言: 事件委托应用在很多开发场景之中,但是很多同学对委托的原理、特别是对JS原生实现委托不太了解。每每看到此情此景我总觉得“众生皆苦”,正所谓“我不写文章,谁写文章”的普渡心态,是以提供这篇文章解救众生之苦,阿弥陀佛! 释义 在学事件委托时,我们有必要先对事件委托做一个定义。 JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。 再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。 作用 在学它的用法和原理之前,我们先了解一下它的作用,有什么好处。再让各位决定是否愿意继续看下去呢? 作用1:节约内存(哇塞,这个好这个棒!) 作用2:能为之后新增的DOM元素依然添加事件(路人甲:这什么鬼?我:死相,真猴急。往后面看就知道了!) 揭开事件委托面纱场景1:当多个li标签需要添加点击事件时代码如图: 代码解析: 给5个li标签加了点击事件,当界面上点击li时,会打印它们各自li标签显示的内容。 出现的问题: 此时5个li,看起来每个li的点击事件触发时调用的都是同一个函数,即: [JavaScript] 纯文本查看 复制代码 function(){ 但其实并不是这样。每个li绑定的都是一个全新的函数,只不过每个函数的样子都一毛一样。 如何验证这个结论呢?很简单,判断每个li标签的onclick是否相等就可以了 代码验证如下: 得到结果: 至于上面说的函数长得一样,但不是同一个数据这种情况就类似于 var obj1 = {name:"jack",age:16}和var obj2 = {name:"jack",age:16},obj1 == obj2 得到的结果也会是false(如对这一块不理解,下次有空再写一篇文章解答。或者来黑马程序员实地学习,课程里有讲) 至此,我们可以得到结论,如果有5个li,那么就有5个函数会被创建在内存中占据空间,那如果有100个li呢?就会有100个长相一毛一样的函数在内存中常驻,对内存的开销是巨大的! 解决办法: 利用事件冒泡的原理,把事件加在父元素(ul)身上! 代码如下: 回顾事件冒泡 事件冒泡:即一个元素的事件触发后,会依次一级一级往上调用父级元素的同名事件,直到window(注:IE8和之前的浏览器只到document) 例:div > p > span 当div和p以及span都添加了click事件,当点击span时,会依次向上触发span、p、div的click事件。 代码如下: 效果如下: 其中,在每个触发的事件里,通过事件对象.target能拿到 触发事件的源头元素 也就是 事件源。 因此,在上述代码中,改成 可发现,触发事件时,打印出来的e.target都是span,如下:
解释委托原理 在回顾完事件冒泡后,我们显而易见得到结论:给所有li添加点击事件,只要加到它们的父元素ul身上的根本原因是利用了事件冒泡。也即:无论点击哪个li,都会自动触发ul的点击事件,然后在ul里通过e.target能获得真正被点击的那个li,继而拿到它的innerHTML 存在问题及解决方式
答案是否定的,因为根据事件冒泡原理,所有子元素点击后都会触发父元素的点击,因此,如果你点击了span,也会调用ul的点击事件,这就相当于给span也加了点击事件。这时候该怎么解决呢? 很简单,只要判断一下事件源是不是li就行了,如果是li才执行代码,否则不执行,代码如下: 场景2: 新增元素没有绑定事件的问题问题描述: 我们先尝试不用事件委托普通写法会怎么写,代码和界面如下: JS部分代码如下: 此时会发现:页面刚开始加载时就默认存在的5个li是有点击事件的,但是点击按钮创建出来的li没有点击事件。 原因剖析: 因为上面的JS代码是在页面刚加载时执行的,在当时因为不可能去点击按钮,所以能找到的li标签只有默认那5个,因此你打印liList,发现也只有5个 因此,你遍历liList给每个元素加点击事件时,只能给这5个li加到点击事件。因此,如果后面再有li标签,也不拥有点击事件。 使用事件委托解决代码如下: 把事件只加在ul身上,即可解决,这样内存占用更低,代码也少了很多!效果如图:
解析:因为事件冒泡机制的存在,不管是原本有的li还是新创建的li,当事件触发时都会一级一级往上调用父元素的同名事件。因此,只要是点击的li标签,都会触发ul的点击事件,所以只要把事件加在ul身上就解决了不管新旧li标签都有点击事件的问题。 jQuery里的事件委托 众所周知,jQuery是JS的一个伟大的第三方库(什么?你还不知道?火星了吧!赶紧来黑马程序员恶补!)。JS有的方法,jQuery里都有,并且代码写起来更短。因此事件委托,其实在jQuery里也存在! jQuery事件委托语法: $('父元素').on('事件名','哪个子元素触发',传给回调函数的参数,事件触发时的回调函数);
例: 说明: 1.on这个方法的参数3可以通过回调函数里的e.data拿到(但一般不会用,大家了解一下有这么个东西即可) 2.在jQuery事件委托的回调函数里this和e.target是同一个东西,但是在JS里this和e.target不是同一个东西(有兴趣可以参考以前的文章或者黑马程序员前端课程) 一般参数3不会写,因此代码常见会写成这样: 结语 其实在其他语言里,事件委托会比较复杂,需要创建额外对象。但是由于JS的灵活性,使得在JS里仅仅只需要把事件绑定在父元素即可实现。 事件委托虽然在面试题中略微少见,但是在实际开发中几乎都会用到。因为有时候需要给某一类元素加事件(例如给所有li加点击事件),因为网页内容经常改变,这类元素随时会增加或者减少,为了保证所有这类元素都有事件,也为了节约内存,所以都需要采用事件委托才可实现! 最后含着泪,抠着脚跟大伙say byebye,我们下期再见~! |
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |