事件机制与事件委托

2 阅读4分钟

回调函数: 事件被触发的时候要执行的操作放在回调函数里

事件对象: 事件的完整信息

事件流: 当点击一个按钮的时候实际上window到按钮元素一整条路上的元素都被点击了,比如侧边栏某一条上有一个按钮,点击这个按钮并不仅仅是点击了这个按钮元素,侧边栏这一条也被点击了,所以事件流分为三个阶段,捕获->目标->冒泡,设置事件监听的同时可以设置该事件在冒泡阶段触发(false,默认)还是在捕获阶段触发(true),即 绑定事件的元素.addEventListener(事件名(比如'click'),处理函数,useCapture),useCapture指定在捕获阶段触发还是冒泡阶段触发,如果不写就是默认false,冒泡阶段,true就是捕获阶段,但是注意目标触发是独立与useCapture之外的,不管这个useCapture设置的是什么,如果当前元素是目标元素的话这个函数都一定会执行,一个元素一个事件可以设置多个监听器,如果当前是捕获阶段会触发捕获监听器,冒泡阶段触发冒泡监听器,如果当前元素就是目标元素那所有的监听器都会按照注册顺序依次执行

  1. 捕获阶段: 事件从window一路传递到目标元素父元素

  2. 目标阶段: 事件到达目标元素

  3. 冒泡阶段: 事件从目标元素父元素一路回到window

  4. 大部分事件支持完整三个阶段如click,mousedown,keydown,change等,部分事件不支持冒泡,如blur,focus,mouseenter,mouseleave,scroll,load,unload等,没有无捕获阶段的事件,因为没有捕获阶段就无法沿着dom树传递到目标节点

  5. tips:

    1. 如果想捕获和冒泡阶段都执行,设置两个相同addEventListener分别设置为true和false就行

    2. 如果想让当前元素在捕获和冒泡阶段都不执行,只有为目标元素的时候才执行,有三种方式

      1. e.target确认触发事件的目标元素是不是元素本身(处理函数接收e参数,默认是事件对象,e.target是触发事件的目标元素,用e.target===this可以校验是否是当前元素触发的事件,如果不是说明是冒泡或者捕获阶段,就不执行,如果为true就说明是目标元素,可以执行),注意,this的绑定在箭头函数中是错误的,所以如果要这么写必须写成非箭头函数
      2. e.eventPhase确认当前是事件流哪个阶段,e.eventPhase是事件流当前阶段,有三个值,1是捕获阶段,2是目标阶段,3是冒泡阶段,用e.eventPhase===2确认当前是否是目标阶段,不是就不执行
      3. e.stopPropagation() 阻止后续的捕获和冒泡,比如现在有5层div由外到内依次是12345全都有click事件的捕获触发和冒泡触发,现在在2的捕获触发的回调函数中调用这个,那么它之后(不包含) 的click捕获和冒泡均不触发,即34的捕获,4321的冒泡全都不触发,如果在2的冒泡触发的回调函数中调用这个,那么它之后的1的冒泡不触发
      4. e.stopImmediatePropagation() ,除了stopPropagation的作用外,还有一个作用是会阻止当前元素相同事件相同阶段的其他所有监听器,但是如果当前元素是目标元素则会阻止当前元素相同事件的其他所有监听器,无论是捕获还是冒泡

事件委托(也叫事件代理) : 利用事件流和e.target,将子元素事件绑定在父元素上(相当于把这个事件委托给父元素),用e.target获取实际触发事件的目标元素,比如列表不需要给每一个项li绑定click,而是在父元素ul上绑定点击事件,然后通过e.target辨认实际触发事件的目标li,(e.target是实际触发事件的目标元素,e.currentTarget是当前正在处理事件的元素),有很多好处

  1. 减少内存占用,不需要每一个子元素绑定一个,只需要在父元素绑定一个监听器
  2. 支持动态元素: 新增子元素无需重新绑定事件,父元素的事件逻辑会自动生效
  3. 减少重复代码