DOM事件事件委托

155 阅读5分钟

DOM事件

什么是DOM?

DOM 全称是Document Object Model,即文本对象模型,是用来呈现以及与任意HTML或XML文档交互的API
DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分

什么是事件?

事件是某个行为或者触发,比如点击,鼠标移动
当用户点击鼠标时
当网页已加载时
当图像已加载时
当鼠标移动到元素上时
当用户触发按键时

点击事件

函数调用顺序
<div class= 爷爷>
  <div class= 爸爸>
    <div class= 儿子>文字</div>
  </div>
</div>
给三个div分别添加事件监听 fnYeye/fnBaba/fnErzi
问题1:点击文字算不算点击了其他div   
答:算点击了其他div
问题2:点击文字,函数的调用顺序 1)fnYeye->fnBaba->fnErzi 2)fnErzi->fnBaba->fnYeye
答:两种调用顺序都行

事件捕获和事件冒泡

浏览器支持两种调用顺序

首先按爷爷-爸爸-儿子顺序看有没有函数监听
然后按儿子-爸爸-爷爷顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过

从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡

当一个事件发生以后,它会在不同的DOM节点之间传播(propagation)这种传播分为三个阶段:
第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)
第二阶段:在目标节点上触发,称为“目标阶段”(target phase)
第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)
先捕获再冒泡。开发者可以自己选择把监听函数放在捕获阶段还是冒泡阶段

图示

事件绑定API

baba.addEventListener('click',fn,bool)   

如果bool不传或为falsy,就让fn走冒泡,即当浏览器在冒泡阶段发现baba有fn监听函数,就会调用fn,并提供事件信息
如果booltrue,就让fn走捕获,即当浏览器在捕获阶段发现baba有fn函数监听,就会调用fn,并提供事件信息

图示

中断冒泡

event.stopPropagation(); 
捕获不可取消,冒泡可以取消,但也有不可取消的冒泡

DOM事件级别

DOM级别分为四个级别:DOM0级,DOM1级,DOM2级和DOM3级
DOM事件分为三个级别:DOM 0级事件处理,DOM 2级事件处理和DOM 3级事件处理
由于DOM 1级中没有事件的相关内容,所以没有DOM 1级事件

DOM 0级事件
el.οnclick=function(){}
var btn = document.getElementById('btn');
 btn.onclick = function(){
    alert(this.innerHTML);

当希望为同一个元素/标签绑定多个同类型事件的时候(如给上面的这个btn元素绑定3个点击事件)是不被允许的
DOM0事件绑定,给元素的事件行为绑定方法,这些方法都是在当前元素事件行为的冒泡阶段(或者目标阶段)执行的

DOM 2级事件
el.addEventListener(event-name, callback, useCapture)

event-name: 事件名称,可以是标准的DOM事件
callback:   回调函数,当事件触发时,函数会被注入一个参数为当前的事件对象 event
useCapture: 默认是false,代表事件句柄在冒泡阶段执行

DOM 3级事件
在DOM 2级事件的基础上添加了更多的事件类型
UI事件,当用户与页面上的元素交互时触发,如:load、scroll
焦点事件,当元素获得或失去焦点时触发,如:blur、focus
鼠标事件,当用户通过鼠标在页面执行操作时触发如:dblclick、mouseup
滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
文本事件,当在文档中输入文本时触发,如:textInput
键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
同时DOM3级事件也允许使用者自定义一些事件

自定义事件

button1.addEventListener('click', ()=>{
  const event = new CustomEvent("frank", {"detail":{name:'frank', age: 18},
  bubbles: true      //可以自动冒泡
  cancelable: false  //不能阻止冒泡
  })
  button1.dispatchEvent(event)
})
button1.addEventListener('frank', (e)=>{
  console.log('frank 事件触发了')
  console.log(e.detail)
})

事件委托代码

思路:点击span后,递归遍历span的祖先元素,看其中有没有ul里面的li

function delegate(element, eventType, selector, fn) {
  element.addEventListener(eventType, e => {
    let el = e.target
    while (!el.matches(selector)) {
      if (element === el) {
        el = null
        break
      }
      el = el.parentNode
    }
    el && fn.call(el, e, el)
  })
  return element
 }

总结:事件委托

通俗地来讲,就是把一个元素响应事件(click、keydown...)的函数委托到另一个元素

一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素
当事件响应到需要绑定的元素上时,会通过事件冒泡机制触发它的外层元素的绑定事件上,然后在外层元素上去执行函数

举例,比如老师让每个同学上台领取成绩单,一种方法就是让每个同学排队领取
另一种方法就是把这件事情委托给学习委员,让学习文员按名字分发给每个人

在这里,取成绩单就是一个事件,每个同学指的是需要响应事件的 DOM 元素
而出去统一领取成绩单的学习委员就是代理的元素,所以真正绑定事件的是这个元素
按照姓名分发成绩单的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个

详细资料点击:事件及DOM