DOM 事件模型或 DOM 事件机制

276 阅读4分钟

事件机制

事件是在编程时系统内发生的动作或者发生的事情,系统会在事件出现的时候触发某种信号并且会提供一个自动加载某种动作的机制(来自MDN)。
每个事件都有事件处理器(有时也叫事件监听器),也就是触发事件时运行的代码块。严格来说事件监听器监听事件是否发生,然后事件处理器对事件做出反应。

DOM事件流

事件流又称为事件传播,DOM2级事件规定的事件流包括三个阶段:事件捕获阶段(capture phase)、处于目标阶段(target phase)和事件冒泡阶段(bubbling phase)

首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应

事件冒泡、事件捕获

事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同 一事件,自子元素冒泡向父元素。(自底向上),比如说:

<style> 
 .wrapper { 
 width: 300px; 
 height: 300px; 
 background-color: red; 
 } 
 .content { 
 width: 200px; 
 height: 200px; 
 background-color: green; 
 } 
 .box { 
 width: 100px; 
 height: 100px; 
 background-color: orange; 
 } 
 </style> 
<body> 
 <div class="wrapper"> 
 <div class="content"> 
 <div class="box"></div> 
 </div> 
 </div> 
 <script> 
 let wrapper = document.getElementsByClassName("wrapper")[0]; 
 let content = document.getElementsByClassName("content")[0]; 
 let box = document.getElementsByClassName("box")[0]; 
 wrapper.addEventListener("click", function () { 
 console.log("wrapper"); 
 }, false) 
 content.addEventListener("click", function () {
 console.log("content"); 
 }, false) 
 box.addEventListener("click", function () { 
 console.log("box"); 
 }, false) 
 </script> 
</body>

如图,然后在 js 里把他们选出来并绑定事件,就是打印各自的class 名,然后当我点击红色就打印 wrapper,但是当我点击绿色的时候,他会按顺序打印出 content wrapper,然后当我点击黄色的时候,他会按顺序打印出 box content wrapper。好像他执行的顺序是漏下去的,因为我点黄色的时候绿色的也触发了,然后红色的也触发了。我们把这种现象叫事件冒泡,事件像水泡一样一层一层往上冒。这个往上冒泡不是视觉上的,是结构上的,就是如果说你事件点击到了子元素上,他就会一层一层的向父元素上传递这个事件,所以他从代码的角度来说是自底向上的。现在我把样式改一下,他就变成了这个样子

.content { 
 margin-left: 300px; 
 width: 200px; 
 height: 200px; 
 background-color: green; 
 } 
 .box { 
 margin-left: 200px; 
 width: 100px; 
 height: 100px; 
 background-color: orange; 
 }

现在他们视觉上已经不嵌套了,但是结构上依然嵌套着,我现在点黄的他依然打印 box content wrapper,所以事件冒泡是存在于代码结构上的,和视觉上没关系

事件捕获:结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同 一事件,自父元素捕获至子元素(事件源元素)。(自顶向下)(只有谷歌实现了,IE 没 有捕获事件) 首先,一个对象的一个事件类型上边绑定的一个处理函数只能遵循一种事件模型,要 么冒泡,要么捕获,不可能冒泡和捕获同时存在。那么,怎么触发事件捕获呢?在addEventListener()这个方法的最后一个参数是false,你把它改为 true就触发了:

<script> 
 let wrapper = document.getElementsByClassName("wrapper")[0]; 
 let content = document.getElementsByClassName("content")[0]; 
 let box = document.getElementsByClassName("box")[0]; 
 wrapper.addEventListener("click", function () { 
 console.log("wrapper"); 
 }, true) 
 content.addEventListener("click", function () { 
 console.log("content"); 
 }, true) 
 box.addEventListener("click", function () { 
 console.log("box"); 
 }, true) 
</script>

捕获和冒泡正好相反,比如我点红的就打印 wrapper,点绿的就按顺序打印 wrapper content,点黄的就打印 wrapper content box,捕获就是先把最外层的先抓住,然后在往子元素一层一层捕获。但是当我点黄色的时候,先是红色捕获并执行,再是绿色捕获执行,最后黄色不是捕获,现在就正常执行。 给一个对象的一个事件类型上边绑定两个处理函数,一个是捕获,一个是 冒泡,那么,谁先执行呢?

  • 第一种情况
<script> 
 let wrapper = document.getElementsByClassName("wrapper")[0]; 
 let content = document.getElementsByClassName("content")[0]; 
 let box = document.getElementsByClassName("box")[0]; 
 wrapper.addEventListener("click", function () { console.log("wrapper"); 
 }, true) 
 content.addEventListener("click", function () { 
 console.log("content"); 
 }, true) 
 box.addEventListener("click", function () { 
 console.log("box");
 }, true) 
 wrapper.addEventListener("click", function () { 
 console.log("wrapperBubble"); 
 }, false) 
 content.addEventListener("click", function () { 
 console.log("contentBubble"); 
 }, false) 
 box.addEventListener("click", function () { 
 console.log("boxBubble"); 
 }, false) 
</script>

现在我点黄的依次打印 wrapper content box boxBubble contentBubble wrapperBubble,是先捕获后冒泡

  • 第二种情况
<script> 
 let wrapper = document.getElementsByClassName("wrapper")[0]; 
 let content = document.getElementsByClassName("content")[0]; 
 let box = document.getElementsByClassName("box")[0]; 
 wrapper.addEventListener("click", function () { 
 console.log("wrapperBubble"); 
 }, false) 
 content.addEventListener("click", function () { 
 console.log("contentBubble"); 
 }, false) 
 box.addEventListener("click", function () { 
 console.log("boxBubble"); 
 }, false) 
 wrapper.addEventListener("click", function () { 
 console.log("wrapper"); 
 }, true) 
 content.addEventListener("click", function () { 
 console.log("content"); 
 }, true) 
 box.addEventListener("click", function () {
 console.log("box"); 
 }, true) 
 </script>

我再点黄的依次打印 wrapper content boxBubble box contentBubblewrapperBubble,看第三位和第四位,咋回事?黄色正常执行,顺序的确是先捕获后冒泡,他的顺序是红的先捕获,然后绿的捕获,然后是黄的正常执行,黄的再执行,再绿色冒泡,再红的冒泡。黄色的是正常执行,那么就是谁先绑定谁就先执行。

总结:触发顺序是先捕获后冒泡