首页技术文章正文

事件委托是什么? JS事件代理的原理

更新时间:2020-10-20 来源:黑马程序员 浏览量:

引言:

事件委托应用在很多开发场景之中,但是很多同学对委托的原理、特别是对JS原生实现委托不太了解。每每看到此情此景我总觉得“众生皆苦”,正所谓“我不写文章,谁写文章”的普渡心态,是以提供这篇文章解救众生之苦,阿弥陀佛!

释义

在学事件委托时,我们有必要先对事件委托做一个定义。

JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。

再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。

作用

在学它的用法和原理之前,我们先了解一下它的作用,有什么好处。再让各位决定是否愿意继续看下去呢?

作用1:节约内存(哇塞,这个好这个棒!)

作用2:能为之后新增的DOM元素依然添加事件(路人甲:这什么鬼?我:死相,真猴急。往后面看就知道了!)

揭开事件委托面纱

场景1:当多个li标签需要添加点击事件时

代码如图:

事件委托01


代码解析:

给5个li标签加了点击事件,当界面上点击li时,会打印它们各自li标签显示的内容。

出现的问题:

此时5个li,看起来每个li的点击事件触发时调用的都是同一个函数,即:


function(){
    console.log(this.innerHTML);             
};

但其实并不是这样。每个li绑定的都是一个全新的函数,只不过每个函数的样子都一毛一样。

如何验证这个结论呢?很简单,判断每个li标签的onclick是否相等就可以了

代码验证如下:

事件委托02


得到结果:

事件委托03


至于上面说的函数长得一样,但不是同一个数据这种情况就类似于 var obj1 = {name:"jack",age:16}和var obj2 = {name:"jack",age:16},obj1 == obj2 得到的结果也会是false(如对这一块不理解,下次有空再写一篇文章解答。或者来黑马程序员实地学习,课程里有讲)

至此,我们可以得到结论,如果有5个li,那么就有5个函数会被创建在内存中占据空间,那如果有100个li呢?就会有100个长相一毛一样的函数在内存中常驻,对内存的开销是巨大的!

解决办法:

利用事件冒泡的原理,把事件加在父元素(ul)身上!

代码如下:

事件委托04


原理解析:

回顾事件冒泡

事件冒泡:即一个元素的事件触发后,会依次一级一级往上调用父级元素的同名事件,直到window(注:IE8和之前的浏览器只到document)

例:div > p > span 当div和p以及span都添加了click事件,当点击span时,会依次向上触发span、p、div的click事件。

代码如下:

事件委托05


效果如下:

事件委托06


其中,在每个触发的事件里,通过事件对象.target能拿到 触发事件的源头元素 也就是 事件源。

因此,在上述代码中,改成

事件委托07


可发现,触发事件时,打印出来的e.target都是span,如下:

事件委托08

注:事件对象在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才执行代码,否则不执行,代码如下:

事件委托09

场景2: 新增元素没有绑定事件的问题

界面描述:界面上有一个ul里面默认有5个li,并且还有一个按钮,当点击按钮就创建一个新的li,需要不管是默认有的li还是新的li都有点击事件。

问题描述:

我们先尝试不用事件委托普通写法会怎么写,代码和界面如下:

事件委托
事件委托11


JS部分代码如下:

此时会发现:页面刚开始加载时就默认存在的5个li是有点击事件的,但是点击按钮创建出来的li没有点击事件。


原因剖析:

因为上面的JS代码是在页面刚加载时执行的,在当时因为不可能去点击按钮,所以能找到的li标签只有默认那5个,因此你打印liList,发现也只有5个

事件委托12


因此,你遍历liList给每个元素加点击事件时,只能给这5个li加到点击事件。因此,如果后面再有li标签,也不拥有点击事件。


使用事件委托解决

代码如下:

事件委托13


把事件只加在ul身上,即可解决,这样内存占用更低,代码也少了很多!效果如图:

事件委托14


我们可以看到,不管是默认有的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

例:

事件委托14


说明:

1.on这个方法的参数3可以通过回调函数里的e.data拿到(但一般不会用,大家了解一下有这么个东西即可)

2.在jQuery事件委托的回调函数里this和e.target是同一个东西,但是在JS里this和e.target不是同一个东西(有兴趣可以参考以前的文章或者黑马程序员前端课程)

一般参数3不会写,因此代码常见会写成这样:

事件委托15

结语

其实在其他语言里,事件委托会比较复杂,需要创建额外对象。但是由于JS的灵活性,使得在JS里仅仅只需要把事件绑定在父元素即可实现。

事件委托虽然在面试题中略微少见,但是在实际开发中几乎都会用到。因为有时候需要给某一类元素加事件(例如给所有li加点击事件),因为网页内容经常改变,这类元素随时会增加或者减少,为了保证所有这类元素都有事件,也为了节约内存,所以都需要采用事件委托才可实现!

最后含着泪,抠着脚跟大伙say byebye,我们下期再见~!


猜你喜欢:

前端touch事件方向的判断

BFC布局规则介绍,哪些元素会生成BFC?

传智播客web前端培训课程 

黑马程序员web前端培训课程 

分享到:
在线咨询 我要报名
和我们在线交谈!