事件是用户或者浏览器自己执行的某种动作,是文档或者浏览器发生的一些交互瞬间,比如点击(click)按钮等,这里的click就是事件的名称。JS 与 HTML 之间的交互是通过事件实现的,DOM支持大量的事件。
事件分发和DOM事件流
众所周知,DOM 是个树形结构,我们在页面上点击一个元素,事件对象将通过 DOM 事件流确定的 DOM 树进行传播。
事件对象被调度到事件目标。但是在分发之前,必须首先确认事件对象的传播路径。
在事件传播过程,IE 认为应该先从最小的子元素开始查找事件对象,然后往上传给每一级的父元素,这种由内向外的查找事件对象方式,叫事件冒泡 。
而网景认为应该从最顶层的元素开始查找,逐层往下找,这种由外向内的查找事件对象方式,叫事件捕获。
最后,2002年,W3C 发布标准,规定浏览器应该同时支持两种调用顺序,于是有了如图的事件流。
事件流分为三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段。
目标阶段,活动对象到达事件对象的事件目标。如果事件类型指示事件不会冒泡,则该事件对象将在此阶段完成后停止。
开发者可以自由选择将事件对象放在捕获阶段还是冒泡阶段。
事件绑定
- IE 5:element.attachEvent('onclick', fn)// 冒泡
- 网景:element.addEventListener('onclick', fn)// 捕获
- W3C:element.addEventListener('onclick', fn, bool)
如果 bool 不传或为 falsy ,fn 就会走冒泡阶段。即当浏览器进入冒泡阶段发现 element 有 fn 监听函数,就会调用 fn 函数,并提供事件信息。
如果 bool 为 true ,fn 就会走捕获阶段。即当浏览器进入捕获阶段发现 element 有 fn 监听函数,就会调用 fn 函数,并提供事件信息。
注意:e 对象被传给所有监听函数。事件结束后,e 对象就不存在了。
target v.s currentTarget
区别
e.target
是用户操作的元素e.currentTarget
是程序员监听的元素
特例
当只有一个 div 被监听,fn 分别在捕获阶段和冒泡阶段监听 click 事件
div.addEventListener('click', f1)
div.addEventListener('click', f2, true)
此时,谁先监听就会先执行。
取消冒泡和不可阻止默认动作
取消冒泡
捕获接断不可取消,但是冒泡阶段可以取消。我们通过 e.stopPropagation()
可以中断冒泡,浏览器不再向上走。一般用于封装某些独立的组件。
不可阻止默认动作
有些事件不能阻止默认动作。以 scroll event
为例,看到 Bubbles 和 Cancelable。
- Bubbles 为该事件是否冒泡,所有冒泡都可取消
- Cancelable 为开发者是否可以阻止默认事件
自定义事件
我们也可以自定义事件
element.addEventListener('click', () => {
const event = new CustomEvent('custom', {
"detail": {name: "custom", age: 18}
})
element.dispatchEvent(event)
})
element.addEventListener('custom', (e) => {
console.log('custom')
console.log(e)
})