事件流表示的是事件在页面中传播的顺序,现代浏览器都遵从一套通用的事件流标准,包括 捕获流 与 冒泡流 。
事件描述的是发生在浏览器里的动作,这个动作可以由用户从外部触发,也可以是浏览器内部逻辑触发。
document.addEventListener(event, function, useCapture)
event: 事件类型,像点击(click)、鼠标悬停(mouseover)、鼠标移走(mousemove)等。
function: 事件触发后执行的函数
useCapture: 事件的执行时机 :
- false: 默认, 事件在冒泡阶段执行 ;
- true: 事件在捕获阶段执行
为了方便的表示事件在元素中的两种传递方式,我们新建如下html页面
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<style>
#out{
background-color: red;
width:300px;
height: 300px;
}
#middle{
background-color: green;
width:200px;
height: 200px;
}
#in{
background-color: blue;
width:100px;
height: 100px;
}
style>
head>
<body>
<div id="out">
<div id="middle">
<div id="in">
div>
div>
div>
<script>
script>
body>
html>
当事件被触发时,首先经历捕获流:事件会从最外层
的元素开始逐元素
到目标元素;然后事件从目标元素原路返回
,进入到冒泡流—— 逐个元素冒到最外层。
<script>
const div_out = document.getElementById('out')
const div_middle = document.getElementById('middle')
const div_in = document.getElementById('in')
// 事件冒泡流
div_out.addEventListener('click', function(){
console.log('冒泡----div_out--red')
}, false)
div_middle.addEventListener('click', function(){
console.log('冒泡----div_middle--green')
}, false)
div_in.addEventListener('click', function(){
console.log('冒泡----div_in--blue', )
}, false)
</script>
<script>
const div_out = document.getElementById('out')
const div_middle = document.getElementById('middle')
const div_in = document.getElementById('in')
// 事件捕获流
div_out.addEventListener('click', function(){
console.log('捕获----div_out--red')
}, true)
div_middle.addEventListener('click', function(){
console.log('捕获----div_middle--green')
}, true)
div_in.addEventListener('click', function(){
console.log('捕获----div_in--blue', )
}, true)
</script>
同样是点击蓝色区域,输出如下
当 DOM 事件处理函数触发时,就会产生一个事件对象 event 作为处理函数的入参。这个对象中囊括了与事件有关的信息,比如事件具体是由哪个元素所触发、鼠标点击位置,触发事件的元素对象等。
div_in.addEventListener('click', function(e){
console.log('捕获----div_in--blue', e)
}, true)
在 event 上有两个重要的属性 currentTarget、 target
除此之外,在事件对象的原型链向上寻找,还有两个非常重要的方法 preventDefault 、stopPropagation
在前面简单介绍过:e.target 是指触发事件的具体元素,就算事件处理函数没有绑定在目标元素上、而是绑定在了目标元素的父元素上,由于冒泡机制的存在,目标元素仍然是冒泡到父容器上触发的,仍然可以通过父元素的 target 来感知到目标子元素
。
利用事件的冒泡特性,把多个子元素的同一类型的监听逻辑,合并到父元素上通过一个监听函数来管理的行为,就是事件代理
如下所示,假设我想每点击一个元素就打印该元素的文本内容,给全部的 li 标签都注册同一个事件当然可以,但是这种做法不仅代码逻辑冗余,而且浪费开销。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<style>
#li-wrap{
background-color: azure;
}
style>
head>
<body>
<ul id="li-wrap">
<li>这是第一行li>
<li>这是第二行li>
<li>这是第三行li>
<li>这是第四行li>
<li>这是第五行li>
<li>这是第六行li>
<li>这是第七行li>
ul>
<script>
script>
body>
html>
点击任何一个 li 标签时,该点击事件都会被冒到它们共同父元素上,id为 li-wrap 的父元素不仅能感知到子元素的事件
,还可以通过 e.target 拿到实际触发事件的具体子元素
,实现事件代理。因此我们可以这么做:
<script>
const parent = document.getElementById('li-wrap')
parent.addEventListener('click', function(e) {
console.log(e.target.innerHTML, e)
})
script>
事件流
事件与事件监听
事件捕获、事件冒泡
事件对象
事件对象的属性
事件对象的方法
事件代理