引言: 事件委托应用在很多开发场景之中,但是很多同学对委托的原理、特别是对JS原生实现委托不太了解。每每看到此情此景我总觉得“众生皆苦”,正所谓“我不写文章,谁写文章”的普渡心态,是以提供这篇文章解救众生之苦,阿弥陀佛! 释义 在学事件委托时,我们有必要先对事件委托做一个定义。 JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。 再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。 作用 在学它的用法和原理之前,我们先了解一下它的作用,有什么好处。再让各位决定是否愿意继续看下去呢? 作用1:节约内存(哇塞,这个好这个棒!) 作用2:能为之后新增的DOM元素依然添加事件(路人甲:这什么鬼?我:死相,真猴急。往后面看就知道了!) 揭开事件委托面纱场景1:当多个li标签需要添加点击事件时代码如图:
代码解析: 给5个li标签加了点击事件,当界面上点击li时,会打印它们各自li标签显示的内容。 出现的问题: 此时5个li,看起来每个li的点击事件触发时调用的都是同一个函数,即:
[JavaScript] 纯文本查看 复制代码 function(){
console.log(this.innerHTML);
}; 但其实并不是这样。每个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,如下: 注:事件对象在ie8中要通过window.event才能取到,因此e = e || window.event是做兼容处理(详细了解请翻阅黑马程序员前端课程关于事件对象的讲解)
解释委托原理 在回顾完事件冒泡后,我们显而易见得到结论:给所有li添加点击事件,只要加到它们的父元素ul身上的根本原因是利用了事件冒泡。也即:无论点击哪个li,都会自动触发ul的点击事件,然后在ul里通过e.target能获得真正被点击的那个li,继而拿到它的innerHTML 小结:如果给一堆元素加事件,并且事件触发时执行的代码都差不多时,就可以把事件加在父元素身上啦!这样可以更节省内存空间哦!O(∩_∩)O哈哈~(来自抠脚大汉的卖萌符号) 看,这样的形式是不是就相当于把自己的事件,委托在父元素身上处理了呢?因而它才会叫事件委托! 也就是:自己的活不干了,给它爹去干!(画外音:一看就是坑爹的货~) 存在问题及解决方式思考:如果ul里还有其他子元素例如span,可我只想给li加点击事件,用原来写的事件委托还行吗?
答案是否定的,因为根据事件冒泡原理,所有子元素点击后都会触发父元素的点击,因此,如果你点击了span,也会调用ul的点击事件,这就相当于给span也加了点击事件。这时候该怎么解决呢? 很简单,只要判断一下事件源是不是li就行了,如果是li才执行代码,否则不执行,代码如下:
场景2: 新增元素没有绑定事件的问题界面描述:界面上有一个ul里面默认有5个li,并且还有一个按钮,当点击按钮就创建一个新的li,需要不管是默认有的li还是新的li都有点击事件 问题描述:我们先尝试不用事件委托普通写法会怎么写,代码和界面如下: JS部分代码如下:
此时会发现:页面刚开始加载时就默认存在的5个li是有点击事件的,但是点击按钮创建出来的li没有点击事件。 原因剖析: 因为上面的JS代码是在页面刚加载时执行的,在当时因为不可能去点击按钮,所以能找到的li标签只有默认那5个,因此你打印liList,发现也只有5个 因此,你遍历liList给每个元素加点击事件时,只能给这5个li加到点击事件。因此,如果后面再有li标签,也不拥有点击事件。 使用事件委托解决代码如下:
把事件只加在ul身上,即可解决,这样内存占用更低,代码也少了很多!效果如图: 我们可以看到,不管是默认有的5个li还是后面才增加的li都有点击事件了
解析:因为事件冒泡机制的存在,不管是原本有的li还是新创建的li,当事件触发时都会一级一级往上调用父元素的同名事件。因此,只要是点击的li标签,都会触发ul的点击事件,所以只要把事件加在ul身上就解决了不管新旧li标签都有点击事件的问题。 jQuery里的事件委托 众所周知,jQuery是JS的一个伟大的第三方库(什么?你还不知道?火星了吧!赶紧来黑马程序员恶补!)。JS有的方法,jQuery里都有,并且代码写起来更短。因此事件委托,其实在jQuery里也存在! jQuery事件委托语法:
$('父元素').on('事件名','哪个子元素触发',传给回调函数的参数,事件触发时的回调函数);解释: 参数1:事件名,代表要绑定什么事件,但是记得不用加on,也就是说如果你想加点击事件,只要写'click'即可,注意是字符串!所以要打单引号或者双引号 参数2:只能由哪个子元素触发,例如我写 "li",就代表只能由这个父元素里面的li触发事件,其他子元素不会触发。需要注意的是,这也是字符串,并且,参数2可以不写,那就代表仅仅只是给父元素加一个点击事件,并且所有子元素都能触发到这个事件(因为事件冒泡) 参数3:其实一般不会用,就是如果想事件触发时,自己给回调函数传一些值就写,这个参数也可以不写! 参数4:事件触发时的回调函数 总结:参数1和参数4是必须的,其他是可选的,如果你要用事件委托,请写上参数2
例: 说明: 1.on这个方法的参数3可以通过回调函数里的e.data拿到(但一般不会用,大家了解一下有这么个东西即可) 2.在jQuery事件委托的回调函数里this和e.target是同一个东西,但是在JS里this和e.target不是同一个东西(有兴趣可以参考以前的文章或者黑马程序员前端课程) 一般参数3不会写,因此代码常见会写成这样:
结语 其实在其他语言里,事件委托会比较复杂,需要创建额外对象。但是由于JS的灵活性,使得在JS里仅仅只需要把事件绑定在父元素即可实现。 事件委托虽然在面试题中略微少见,但是在实际开发中几乎都会用到。因为有时候需要给某一类元素加事件(例如给所有li加点击事件),因为网页内容经常改变,这类元素随时会增加或者减少,为了保证所有这类元素都有事件,也为了节约内存,所以都需要采用事件委托才可实现! 最后含着泪,抠着脚跟大伙say byebye,我们下期再见~!
|