Kasan Blog

运行代码:运行 | 这是源码:源码

DOM事件模型

先经历从上到下的捕获阶段,再经历从下到上的冒泡阶段。

addEventListener(‘eventName’, fn , false/true )

第三个参数可以选择阶段,默认是false ( 冒泡 内→外 ) true 是 捕获 外→内

可以使用event.stopPropagation()来阻止捕获或冒泡。

DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口

该接口主要提供三个实例方法:

addEventListener:绑定事件的监听函数
removeEventListener:移除事件的监听函数
dispatchEvent:触发事件

事件传播

<div class="grandfather">
  <div class="father">
    <div class="son">
    <button>事件按钮</button>
    </div>
  </div>
</div>

给三个div分别添加事件的监听fnYe/fnBa/fnEr

点击事件按钮, grandfather , father , son算不算都被点击了?

答案是算的。


那么监听的事件执行顺序怎么决定?

由DOM事件传播的三个顺序阶段决定:捕获阶段,目标阶段,冒泡阶段

1.由外向内找监听函数就是事件捕获 fnYe->fnBa->fnEr

2.在目标节点触发事件

3.由内而外找监听函数就是事件冒泡 fnEr->fnBa->fnYe


历史小知识:

当年IE认为应该从内而外(冒泡)

而网景认为要从内而外(捕获)

两家公司争论不休,苦了开发者们

后来W3C出了标准,要求先捕获再冒泡

IE5*:baba.attachEvent(‘onclick’,fn)//冒泡

网景:baba.addEventListener(‘click’,fn)//捕获

W3C:baba.addEventListener(‘click’,fn,bool)

事件参考:https://developer.mozilla.org/zh-CN/docs/Web/Events


EventTarget.addEventListener()(‘click’,fn,bool)

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

如果bool指定为true
就让fn走捕获,即当浏览器在捕获阶段发现baba有fn监听函数,就会调用fn,并且提供事件信息

DOM事件模型图示


target 和 currentTarget的区别

e.target - 用户操作的元素
e.currentTarget-程序员监听的元素
this是e.currentTarget,我个人不推荐使用它

比如:
div>span{文字},用户点击文字
e.target就是span
e.currentTarget就是div

e.stopPropagation():取消冒泡

e.stopPropagation()可打断冒泡,浏览器不再向上走

一般用于封装某些独立组件

注意:捕获不可以取消但是冒泡可以


有些事件不可以取消冒泡

比如scroll

Bubbles:冒泡

Cancelable:能否取消冒泡


事件委托

概念: 事件委托也叫事件代理,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。

假设现在要给100个按钮上监听:点击不同的按钮时打印出按钮的编号

代码:

//html
div>button{按钮 $}*100
//js
div1.addEventListener('click',(e)=>{
  const t = e.target
  if(t.tagName.toLowerCase() === 'button'){
    console.log('button 被点击了')
    console.log('button的编号是'+ t.textContent)
  }
})

我们可以通过监听所有button元素的父元素div1,当target为button时,打印出编号

示例

事件委托的优点:

1.可以大量节省内存占用,减少事件注册。

2.可以实现当新增子对象时无需再次对其绑定(动态绑定事件)