浅谈JS事件模型

2,708 阅读6分钟

在JavaScript中,事件模型是非常重要的一个知识点,在这里我将对事件模型做一个浅薄的分析。了解事件模型之前先让我们来了解一下什么是事件,事件(Event)是JavaScript应用跳动的心脏 ,也是把所有东西粘在一起的胶水。当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了,它是JS与HTML之间交互的桥梁。

DOM介绍

DOM

DOM全拼为Document Object Model(文档对象模型)是一种用于HTML和XML文档的编程接口,它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式。


DOM树

DOM实际上是以面向对象方式描述的文档模型。DOM定义了表示和修改文档所需的对象、这些对象的行为和属性以及这些对象之间的关系。可以把DOM认为是页面上数据和结构的一个树形表示如图1所示,不过页面当然可能并不是以这种树的方式具体实现(html -> brower(解析成一棵 DOM树)),js操作树就叫dom操作。


                                                      图1

举个例子:

<body>   
 <div class="wrpper">        
<!-- js插入一些节点 -->   
 </div>    
<script>        
// dom操作        
// 代码都是字符串(一定的格式)        
// html -> brower(解析成一棵 DOM树)->js操作我们这棵树就叫dom操作        
// DOM 标准规定了 这些 api    
const wrpper = document.querySelector('.wrpper');   
wrpper.innerHTML = `    
<li>1</li>    
<li>2</li>    
<li>3</li>    `   
</script>

结果:

     

js操作中插入的li标签会解析成一棵DOM树上的根节点

事件模型的三个阶段

在浏览器发展的过程中,对于事件模型阶段的定义都有不同的标准,如IE:冒泡 bubble,firefox:捕获 capture,最终 w3c制定 统一的规范:capture,bubble 融合(如图一所示),在这里我将做一个浅薄的分析,以鼠标点击事件为例。

冒泡阶段

举个例子:

<!DOCTYPE html>
<html lang="en">
<head>  
<meta charset="UTF-8">  
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>Document</title>  
<style>    
.parent {      
width: 300px;      
height: 300px;      
border: 1px solid #000;    }    
.child {      
width: 100px;      
height: 100px;      
border: 1px solid red;    }  
</style>
</head>
<body>  
<div class="parent">    
parent    
<div class="child">      
child    
</div>  
</div>  
<script>    
const parent = document.querySelector('.parent');    
const child = document.querySelector('.child');    
parent.addEventListener('click', () => {     
console.log('parent 被点击了');   
})    
child.addEventListener('click', (event) => {      
// child 触发 click 的时候 就是 目标阶段      
console.log('child 被点击了');    
})  
</script>
</bodya>


如图二所示当我们点击child的时候,会触发一个事件,像个水中的气泡一样一直往上冒,直到顶端。从DOM树型结构上理解,就是事件由叶子节点沿祖先结点一直向上传递直到根节点(如图一Bubble push),这时事件处于冒泡阶段。


捕获阶段

捕获事件:当我们在 DOM 树的某个节点发生了一些操作(鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。

如何将处于冒泡阶段的事件修改成捕获阶段呢?

parent.addEventListener('click', () => {      
console.log('parent 被点击了');   
},true)    
child.addEventListener('click', (event) => {     
      
console.log('child 被点击了');          
},true)

当处于冒泡阶段时,我们没有必要加第三个参数,因为addEventListener默认第三个参数为false(第三个参数存在),如果我们要事件处于捕获阶段的话,只要将第三个参数设成true就行。

结果:



目标阶段

一般来说,很少人会记得目标阶段,更多的是捕获和冒泡阶段,以例子的代码说明,child 触发 click 的时候 就是 目标阶段。

事件模型的用处

举个例子:

<!DOCTYPE html><html lang="en">
<head>    
<meta charset="UTF-8">    
<meta name="viewport" content="width=device-width, initial-scale=1.0">    
<meta http-equiv="X-UA-Compatible" content="ie=edge">    
<title>Document</title>
</head>
<body>    
<ul>        
<li>111</li>        
<li>222</li>        
<li>34343</li>        
<!-- 3s 之后我们在下面插入了 一个 li -->    
</ul>    
<script>    
// 点击每一个li 输出 li里面的 文本内容    
// 1:常规 只能选中 当前页面上已有的 li 节点,给他们添加事件,后添加来的 节点 是没有效果的    
const lis =document.querySelectorAll('li');    
// 每一个li 都绑定事件    
lis.forEach((li) => {    
li.addEventListener('click',function(){        
console.log(li.innerText);    
})    
})   
 // 定时器    
setTimeout(function(){        
// 插入        
const ul =document.querySelector('ul');        
// 创建一个 li节点:<li></li>        
const li =document.createElement('li');        
li.innerHTML = '4444';        
ul.appendChild(li)    
},3000)    
</script>
</body>
</html>

当我们在页面上定义三个节点时,要求点击每一个li 输出 li里面的 文本内容,这时我们只要将每一个li都绑定事件就行,然后输出就行。

结果:



3s 之后我们在下面插入了 一个 li,再次点击li

结果:



为什么没有出现新插入节点的值?通过分析我们可以知道,当前页面上已有的 li 节点,给他们添加事件,后添加来的 节点 是没有效果的。如果我们要认新出现的节点通过点击节点显示出来,这就要用到js事件模型中的冒泡阶段的知识,借助 冒泡的特点 ,父节点可以监听到子节点有没有发生点击事件(如果子节点点击了,父节点也会收到这个 click事件)。

代码:

const ul =docum .querySelector('ul');        
ul.addEventlistener('click',function(event){        
        
const target = event.target;        
console.log(target.innerText);   
})

上述代码中,我们只需要绑定一次事件,通过Event对象的target属性返回事件源(即事件的目标节点),可以做不同的处理,这就是事件代理,原本需要目标元素处理的事件,交由其父元素代为执行。采用事件代理避免了频繁的操作DOM,优化效果可想而知。

以上只是对事件代理的基本认识和应用,更深层次应用有待挖掘,新手一枚,欢迎指正,最后祝各位前程似锦,变得越来越牛逼2333。