DOM事件机制

188 阅读3分钟

冒泡和捕获

事件冒泡和事件捕获的概念是由微软和网景公司提出的,这两个概念都是为了解决页面中的事件流问题,即事件的发生顺序,首先我们先看一段代码

<div class="grandpa">
<div class ="parent">
<div class ="son">click me</div>
</div>
</div>

如果在点击上面的那个click me的,我们如何判断到底是grandpa,parent还是son的处理函数被先触发呢,为了解决这个问题,我们先来看一下事件冒泡和事件捕获的概念

事件冒泡

事件冒泡指的就是像水里的小泡泡一点点变大然后冒出水面,所以事件会从最内层先触发,然后往外层查找,直到document对象,所以上面的代码的click触发事件的顺序为:son->parent->grandpa->body->html->document

事件捕获

事件捕获是与事件冒泡的监听查找函数顺序相反,所以事件会从最外层开始触发监听函数直到最具体的元素,所以上面代码的click监听事件的顺序为:document->html->body->grandpa->parent->son

w3c的处理方式

w3c为了中和两个事件处理机制,,规定了事件流同时支持事件冒泡和事件捕获,但是可以通过使用addEventListener方法的第三个参数来控制监听函数是在哪个阶段执行

addEventListener(event,function,useCapture)

注意:event(事件名):字符串,不要使用'on'前缀,例如使用'click',不要使用'onclick' useCapture:当它的值为'true'的时候,表示监听函数在捕获阶段执行,当他为false或不传的时候,表示监听函数在冒泡阶段执行

捕获和冒泡额执行顺序

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div class="parent">
  <div class="son">click me</div>
</div>
</body>
</html>

const parent = document.querySelector('.parent')
const son = document.querySelector('.son')
parent.addEventListener('click',(e)=>{
  console.log('parent div 捕获')
},true)
parent.addEventListener('click',(e)=>{
  console.log('parent div 冒泡')
},false)
son.addEventListener('click',(e)=>{
  console.log('son div 捕获')
},true)
son.addEventListener('click',(e)=>{
  console.log('son div 冒泡')
},false)

由结果可以看出,正常情况下(在执行非target节点)一般是先执行捕获然后在执行冒泡,代码 但是有一种特殊情况是,在执行target节点时,执行先注册事件,即冒泡和捕获哪个在前先执行哪个

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div class="test">click me</div>
</body>
</html>
const test = document.querySelector('.test')
test.addEventListener('click',(e)=>{
  console.log('test 冒泡')
},false)
test.addEventListener('click',(e)=>{
  console.log('test 捕获')
},true)

代码

取消冒泡

捕获是不可以取消的,但是冒泡可以,阻止冒泡的方法为给子级加event.stopPropagation(),这里的阻止冒泡不是阻止事件发生,而是阻止向外传递

<div class="parent">
<div class="son">click me</div>
const parent = document.querySelector('.parent')
const son = document.querySelector('.son')
parent.addEventListener('click',(e)=>{
  console.log('parent div 捕获')
},true)
parent.addEventListener('click',(e)=>{
  console.log('parent div 冒泡')
},false)
son.addEventListener('click',(e)=>{
  console.log('son div 捕获')
},true)
son.addEventListener('click',(e)=>{
  console.log('son div 冒泡')
  event.stopPropagation()
},false)

请注意有些冒泡事件可以取消,但是有些是不可以的,例如scroll event,具体请查阅MDN文档,Bubbles,表示该事件是否冒泡,Cancelable表示是否可以取消冒泡