这是属于面试高频问题,今天就来具体说说。
事件委托又名事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。 怎么来理解呢?我们有必要先对事件委托做一个定义。
释义
JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。
再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。
举个栗子(菜鸟驿站)。阿甲, 阿乙, 阿丙三个人 预计会在周一收到快递。签收快递有两种办法:一是三个人在公司门口等快递;二是委托给前台代为签收,之后再去领取。现实中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口等快递)。前台收到快递后,会判断收件人是谁,然后按照收件人的要求签收。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台也会在收到寄给新员工的快递后核实并代为签收。
这里其实还隐含有2层意思:
- 在编员工委托前台的同事是可以代为签收的,即程序中的现有的
DOM节点是有事件的; - 新员工也是可以被前台代为签收的,即程序中新添加的
DOM节点也是有事件的。
作用和优点
作用1:节约内存
作用2:能为之后新增的DOM元素依然添加事件
优点: 可以大量节省内存占用,减少事件注册
原理
利用事件冒泡原理来实现,只指定一个事件处理程序,管理当前绑定下的所有元素的事件。也就是事件从最内层的节点开始,然后逐步向上传播事件,一层一层的往外执行触发响应,执行顺序由内而外
应用背景
场景1:当多个li标签需要添加点击事件时
传统方式
- 获取
ul - 通过获取到
ul去遍历下面的所有li - 遍历的
li,依次去注册(绑定)事件
当用事件委托处理时
-
这里用父级
ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发 -
Event对象提供了一个属性叫
target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用e.target,IE浏览器用event.srcElement。
回顾总结
显而易见得到结论:给所有li添加点击事件,只要加到它们的父元素ul身上的根本原因是利用了事件冒泡。也即:无论点击哪个li,都会自动触发ul的点击事件,然后在ul里通过e.target能获得真正被点击的那个li,继而拿到它的innerHTML
小结:如果给一堆元素加事件,并且事件触发时执行的代码都差不多时,就可以把事件加在父元素身上啦!这样可以更节省内存空间哦!
看,这样的形式是不是就相当于把自己的事件,委托在父元素身上处理了呢?因而它才会叫事件委托!
也就是:自己的活不干了,给它爹去干!
场景2:新增元素没有绑定事件的问题
界面描述:界面上有一个div里面默认有2个p,并且还有一个按钮,当点击按钮就创建一个新的p,需要不管是默认有的p还是新的p都有点击事件。
传统方式
此时会发现:页面刚开始加载时就默认存在的2个p是有点击事件的,但是点击按钮创建出来的p没有点击事件。
原因剖析:因为上面的JS代码是在页面刚加载时执行的,在当时因为不可能去点击按钮,所以能找到的p标签只有默认那2个,因此你打印divps,发现也只有2个
因此,你遍历divps给每个元素加点击事件时,只能给这2个p加到点击事件。因此,如果后面再有p标签,也不拥有点击事件。
使用事件委托解决
可以看到,不管是默认有的2个p还是后面才增加的p都有点击事件了
解析:因为事件冒泡机制的存在,不管是原本有的p还是新创建的p,当事件触发时都会一级一级往上调用父元素的同名事件。因此,只要是点击的p标签,都会触发div的点击事件,所以只要把事件加在div身上就解决了不管新旧p标签都有点击事件的问题。
总结
事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,整体只注册一次div事件,并没有给每个p都注册个监听。这样可以大大的减少dom操作,这就是事件委托的意义。