likes
comments
collection
share

玩转js事件机制

作者站长头像
站长
· 阅读数 21

js事件机制

前言

最近写了个仿饿了么的项目,其中有一个很简单的功能,就是点击一个菜品的增加按钮会让数量加一,而点击这个菜品的任何区域会跳转到这个菜品的详情页,效果图如下:

玩转js事件机制 玩转js事件机制

一个小question交给你: 点击加号按钮是不是也同时点击了这个菜品呢?那效果呈现出来的是不是两个功能会同时生效呢?

想必各位大佬心中已有答案,这也是我们要聊聊的js事件机制问题了👀。

正文

首先,我们要知道js事件流的概念。

一、js事件流

js事件流一共分为三个阶段

  1. 从 window上 事件触发处 传播,遇到 注册的捕获事件 就会触发。
  2. 传播到事件触发处,触发注册的事件。
  3. 从 事件触发处 window上 传播,遇到 注册的冒泡事件 触发。

来张图让你更好的理解:

玩转js事件机制

二、举个例子

我们要实现这个效果,定义三个容器,给三个容器都添加一个点击监听事件,点击相应的容器并输出相应的打印。我们需要观察的是点击相应的容器输出结果

玩转js事件机制

1.第一种情况

addEventListener监听事件只有两个参数时

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #greenBox{
      width: 200px;
      height: 200px;
      background: #c2f5ca;
    }
    #pinkBox{
      width: 100px;
      height: 100px;
      background-color: #f7d0d0;
      color:#fff;  
      position: absolute;
    }
    #blackBox{
      width: 50px;
      height: 50px;
      background: #000;
    }
  </style>

 
</head>
<body>
  <div id="greenBox">
    <div id="pinkBox">
      <div id="blackBox"></div>
    </div>
  </div>
  
  <script> 
    let greenBox=document.getElementById('greenBox')
    let pinkBox=document.getElementById('pinkBox')
    let blackBox=document.getElementById('blackBox')

    greenBox.addEventListener('click',()=>{
      console.log('greenBox')
    })
    pinkBox.addEventListener('click',(event)=>{
      console.log('pinkBox') 
    })
    blackBox.addEventListener('click',(event)=>{
      console.log('blackBox')
    })
  </script>
</body>
</html>

当我们点击黑色容器时,得到的打印结果:

玩转js事件机制

这是为什么呢?

答:因为addEventListener是存在第三个参数的,当没有第三个参数时,默认是遇到注册的冒泡事件触发。 1)当js事件流从window上事件触发处传播的过程中,并没有遇到注册的捕获事件,故不会触发; 2)当js事件流从事件触发处window上传播的过程中,首先遇到的是blackBox注册的的冒泡事件,之后是pinkBox,最后是greenBox

2.第二种情况

当addEventListener监听事件有三个参数时,第三个参数为truefalse。当第三个参数为false时,只在遇到注册的冒泡事件触发;为true时,只在遇到注册的捕获事件触发。

1)我们在greenBox的监听事件中添加第三个参数true,代码如下:

  <script> 
    greenBox.addEventListener('click',()=>{
      console.log('greenBox')
    },true)
    pinkBox.addEventListener('click',(event)=>{
      console.log('pinkBox') 
    })
    blackBox.addEventListener('click',(event)=>{
      console.log('blackBox')
    })
  </script>

当我们点击黑色容器时,得到的打印结果:

玩转js事件机制

这是为什么呢?

答:因为greenBox的addEventListener存在的第三个参数为true,则说明greenBox只在遇到注册的捕获事件触发。 1)当js事件流从window上事件触发处传播的过程中,遇到greenBox注册的捕获事件,故触发greenBox; 2)当js事件流从事件触发处window上传播的过程中,首先遇到的是blackBox注册的的冒泡事件,之后是pinkBox

2)我们在greenBox的监听事件中添加第三个参数false,在pinkBox的监听事件中添加第三个参数true,代码如下:

<script> 
    greenBox.addEventListener('click',()=>{
      console.log('greenBox')
    },flase)
    pinkBox.addEventListener('click',(event)=>{
      console.log('pinkBox') 
    },true)
    blackBox.addEventListener('click',(event)=>{
      console.log('blackBox')
    })
  </script>

当我们点击黑色容器时,得到的打印结果:

玩转js事件机制

这个结果也是在意料之中的,首先pinkBox在遇到注册的的捕获事件触发,而blackBox默认在遇到注册的的冒泡事件触发,greenBox在遇到注册的的冒泡事件触发。

三、阻止默认事件

当我们想只触发其中的一个或多个事件,而其它事件不触发时,我们可以使用stopPropagation()stopImmediatePropagation()函数。

stopPropagation() : 终止默认事件传播到其他容器

stopImmediatePropagation() : 终止默认事件传播到其他容器上 和 自己这个容器的其他事件

四、事件代理

事件代理是js事件机制一个重要的应用

1.来个例子

我们要实现一个列表,其中有五项,点击任何一项输出相应的列表项文本。

我们大多数小白会这么写,采用循环进行遍历:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  li {
    width: 100px;
    background: #efe5ad;
    font-size: 20px;
    margin: 10px;
  }
</style>

<body>
  <ul id="ul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>

  <script>
     let li = document.getElementsByTagName("li");
     for (let i = 0; i < li.length; i++) {
       li[i].addEventListener("click", () => {
         console.log(li[i].innerHTML)
       })
     }
  </script>
</body>

</html>

但是这样做每次循环都需要创建一个监听事件,这就不优雅了,试着利用js事件流优化它一下吧!

<script>
    //事件代理 
    let ul = document.getElementById("ul");
    ul.addEventListener("click", (event) => {
      console.log(event.target.innerHTML);
    })
  </script>

我们只需要获取uldom结构,调用ul监听事件参数event中的taget中的innerHTML就可以得到值啦!

2.优点

  • 只需要把事件绑定到ul上,占用的内存更小
  • 可以动态给添加的元素绑定监听事件,不需要每添加一个元素就重新绑定一次。

最后

我们开头的问题想必大家都有答案了,是因为在菜品这个div中默认设置了阻止默认事件的函数stopPropagation(),当点击按钮时会触发这个按钮的冒泡事件。故点击按钮并不会跳转到这个菜品的详情页。

转载自:https://juejin.cn/post/7232905822279204919
评论
请登录