DOM事件体系:事件流、事件对象、事件代理

文章目录

  • 事件流
    • 事件与事件监听
    • 事件捕获、事件冒泡
  • 事件对象
    • 事件对象的属性
    • 事件对象的方法
  • 事件代理
  • 总结


事件流

事件流表示的是事件在页面中传播的顺序,现代浏览器都遵从一套通用的事件流标准,包括 捕获流冒泡流

事件与事件监听

  • 事件

事件描述的是发生在浏览器里的动作,这个动作可以由用户从外部触发,也可以是浏览器内部逻辑触发。

  • 事件监听函数
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>

DOM事件体系:事件流、事件对象、事件代理_第1张图片

当事件被触发时,首先经历捕获流:事件会从最外层的元素开始逐元素到目标元素;然后事件从目标元素原路返回,进入到冒泡流—— 逐个元素冒到最外层。

DOM事件体系:事件流、事件对象、事件代理_第2张图片

  • 事件冒泡
    为三个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>

点击蓝色区域,输出如下
DOM事件体系:事件流、事件对象、事件代理_第3张图片

  • 事件捕获
    为三个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事件体系:事件流、事件对象、事件代理_第4张图片

事件对象

当 DOM 事件处理函数触发时,就会产生一个事件对象 event 作为处理函数的入参。这个对象中囊括了与事件有关的信息,比如事件具体是由哪个元素所触发、鼠标点击位置,触发事件的元素对象等。

    div_in.addEventListener('click', function(e){
      console.log('捕获----div_in--blue', e)
    }, true)

事件对象的属性

在 event 上有两个重要的属性 currentTargettarget

DOM事件体系:事件流、事件对象、事件代理_第5张图片

  • currentTarget :记录事件当下正在被哪个元素接收
  • target :指触发事件的具体目标,即事件的真正来源。

事件对象的方法

除此之外,在事件对象的原型链向上寻找,还有两个非常重要的方法 preventDefaultstopPropagation

DOM事件体系:事件流、事件对象、事件代理_第6张图片

  • preventDefault :阻止特定事件的默认行为,如 点击 a 标签时禁止跳转
  • stopPropagation:终止事件在传播过程的捕获或冒泡阶段进一步传播

事件代理

在前面简单介绍过:e.target 是指触发事件的具体元素,就算事件处理函数没有绑定在目标元素上、而是绑定在了目标元素的父元素上,由于冒泡机制的存在,目标元素仍然是冒泡到父容器上触发的,仍然可以通过父元素的 target 来感知到目标子元素

利用事件的冒泡特性,把多个子元素的同一类型的监听逻辑,合并到父元素上通过一个监听函数来管理的行为,就是事件代理

如下所示,假设我想每点击一个元素就打印该元素的文本内容,给全部的 li 标签都注册同一个事件当然可以,但是这种做法不仅代码逻辑冗余,而且浪费开销。

DOM事件体系:事件流、事件对象、事件代理_第7张图片


<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>

这样就实现了事件代理
DOM事件体系:事件流、事件对象、事件代理_第8张图片

总结

事件流

  • 事件与事件监听

  • 事件捕获、事件冒泡

事件对象

  • 事件对象的属性

  • 事件对象的方法

事件代理

你可能感兴趣的:(#,DOM,前端,javascript,前端)