js事件流 (捕获阶段、目标阶段、冒泡阶段)取消浏览器的默认冒泡行为

代码敲太久了,以前学时候记得非常清楚的知识,全都记不清了,真的越学越不会

不知道能干多久哟…

在这里插入图片描述

什么是事件流

了解什么是事件流,主要的是了解 ·事件·的概念:
 - 前端页面的交互主要就是由事件驱动来实现的;
 - 而类似于 dom的 click、change,浏览器的 onload, 键盘的 keypress都可以称之为事件。
 - 想要具体了解的话,可以去看《JavaScript权威指南》的第17章。

js的事件流指的是: 
	- 事件的流向: 浏览器响应一个 `事件` 的执行顺序。
	- 主要分为三个阶段(与上图对应):

1. 捕获阶段: 从文档的最底层(document)向上查询事件源,直到查询到事件源,这段时间都是捕获阶段(①~④);
2. 目的阶段: 捕获阶段改变至冒泡阶段的时段(④);
3. 冒泡阶段(处理事件的阶段): 由事件源向下冒泡,直到冒泡文档(document)之后,冒泡结束(④~⑦)。

举个栗子

以一个 click 的简单例子来说明:

<!DOCTYPE html>
<html lang="en" onclick="clickHandle('html', event)">
<head>
  <style>
    #app {
      
      
      background-color: red;
      color: #ccc;
    }
  </style>
</head>

<body onclick="clickHandle('body', event)">
  <div id="app" onclick="clickHandle('#app', event)">
    <button onclick="clickHandle('button', event)">按钮</button>
  </div>
</body>

<script>
  function clickHandle(msg, e) {
      
      
    console.log(`\`${ 
        msg}\` 被点击了`, e);
  }
</script>
</html>
/**
   * 上面的代码主要是给 'html' > 'body' > '#app' > 'button', 添加了一个点击事件 'click',对应的DOM事件触发后会console一条提示信息;
   * 
   * 而根据我们上面说明的事件流向的话:
   *  - 当我们点击了 <button> 按钮,我们的console顺序应该是: 
   *    1. 捕获阶段: html -> body -> #app -> button 
   *    2. 目标阶段:button
   *    3. 冒泡阶段:button -> #app -> body -> html
   * html -> body -> #app -> button -> button -> button -> #app -> body -> html (除button外,每个dom触发两次)
  **/

执行亿下
在这里插入图片描述在这里插入图片描述
就四句输出!?不可能!!我写的代码一定是正确的!!!
垃圾浏览器!淦!!!

浏览器默认事件行为

其实很简单就能想通了。
- 要是事件流的每一步都触发,那每一个dom事件都要触发两次至少,但是普遍我们只需要一次;
- 所以这里的话浏览器普遍默认的是只执行事件流的  '冒泡阶段';
- 能解(忽)释(悠)通了
- 事实就是这样的

触发捕获阶段

如果需要我们的事件在捕获阶段就触发呢:
这个时候就需要用到 ‘addEventListener’ ,设置第三个参数当为 true 时,就触发于捕获阶段;
那我们在给每一个DOM添加一个;
<!DOCTYPE html>
<html lang="en" onclick="clickHandle('html', event)">

<head>
  <style>
    #app {
      
      
      background-color: red;
      color: #ccc;
    }
  </style>
</head>

<body onclick="clickHandle('body', event)">
  <div id="app" onclick="clickHandle('#app', event)">
    <button onclick="clickHandle('button', event)">按钮</button>
  </div>
</body>

<script>
  function clickHandle(msg, e) {
      
      
    console.log(`\`${ 
        msg}\` 被点击了`, e);
  }

  document.querySelector("button").addEventListener('click', (e) => clickHandle('capture: button', e), true);
  document.querySelector("#app").addEventListener('click', (e) => clickHandle('capture: #app', e), true);
  document.querySelector("body").addEventListener('click', (e) => clickHandle('capture: body', e), true);
  document.querySelector("html").addEventListener('click', (e) => clickHandle('capture: html', e), true);
</script>

</html>

运行一下:
在这里插入图片描述
得行
但是有个小问题得留意一下,上面这个是谷歌浏览器运行的结果;
下面是QQ浏览器、 火狐、 谷歌双核浏览器运行的结果
在这里插入图片描述
最后这个目标元素这里有一些小差异,但是问题不大(留意一下)。

项目上可能出现的问题(取消浏览器的默认行为)

  • 元素的嵌套;比如我们例子当中的果仁,不是不是。。。
  • 比如在我们例子当中,点击button只要console,点击#app,只要alert;
  • 但如果按上面这样写的话,
  • 点击#app的时候,正确;
  • 点击button的时候,就会出现console + alert 了(冒泡事件触发) 出错了!!!
  • 这个时候我们是不需要button触发冒泡事件的;所以我们需要取消浏览器的默认行为;
// 阻止冒泡事件
    if (e.stopPropagation) {
    
    
      e.stopPropagation()
    } else {
    
    
      e.cancelBubble = true; // 兼容 IE8-
    }
// vue的话,使用修饰符就可以了 https://cn.vuejs.org/v2/api/#v-on
// <button @click.stop="btnClick">按钮</button>

全部代码

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #app {
      
      
      background-color: red;
      color: #ccc;
    }
  </style>
</head>

<body>

  <div id="app">
    <button>按钮</button>
  </div>
</body>

<script>
  let appDom = document.getElementById("app");
  let btnDom = document.querySelector("button");

  appDom.addEventListener('click', appClick);
  btnDom.addEventListener('click', btnClick);

  function btnClick(e) {
      
      
    e.stopPropagation(); // 马上2022了,微软都自己放弃ie了,就不要让它来祸害我们前端了。。。
    console.log('btn 被点击了')
  }

  function appClick() {
      
      
    alert("app 被点击了");
  }
</script>

</html>

猜你喜欢

转载自blog.csdn.net/cc_King/article/details/121283807