文章目录
- 事件流
- 事件与事件监听
- 事件捕获、事件冒泡
- 事件对象
- 事件对象的属性
- 事件对象的方法
- 事件代理
- 总结
事件流
事件流表示的是事件在页面中传播的顺序,现代浏览器都遵从一套通用的事件流标准,包括 捕获流 与 冒泡流 。
事件与事件监听
- 事件
事件描述的是发生在浏览器里的动作,这个动作可以由用户从外部触发,也可以是浏览器内部逻辑触发。
- 事件监听函数
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>Document</title><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>
当事件被触发时,首先经历捕获流:事件会从最外层
的元素开始逐元素
到目标元素;然后事件从目标元素原路返回
,进入到冒泡流—— 逐个元素冒到最外层。
- 事件冒泡
为三个div逐个注册事件,addEventListener 的第三个参数设置为false(默认值),开启事件冒泡流:
<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>
点击蓝色区域,输出如下👇
- 事件捕获
为三个div逐个注册事件,addEventListener 的第三个参数设置为true,开启事件捕获流:
<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
- currentTarget :记录事件当下正在被哪个元素接收
- target :指触发事件的具体目标,即事件的真正来源。
事件对象的方法
除此之外,在事件对象的原型链向上寻找,还有两个非常重要的方法 preventDefault 、stopPropagation
- preventDefault :阻止特定事件的默认行为,如 点击 a 标签时禁止跳转
- stopPropagation:终止事件在传播过程的捕获或冒泡阶段进一步传播
事件代理
在前面简单介绍过:e.target 是指触发事件的具体元素,就算事件处理函数没有绑定在目标元素上、而是绑定在了目标元素的父元素上,由于冒泡机制的存在,目标元素仍然是冒泡到父容器上触发的,仍然可以通过父元素的 target 来感知到目标子元素
。
利用事件的冒泡特性,把多个子元素的同一类型的监听逻辑,合并到父元素上通过一个监听函数来管理的行为,就是事件代理
如下所示,假设我想每点击一个元素就打印该元素的文本内容,给全部的 li 标签都注册同一个事件当然可以,但是这种做法不仅代码逻辑冗余,而且浪费开销。
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title><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>
这样就实现了事件代理👇
总结
事件流
-
事件与事件监听
-
事件捕获、事件冒泡
事件对象
-
事件对象的属性
-
事件对象的方法
事件代理