面试题: JavaScript事件流与事件代理

  JavaScript中的事件流是指事件在页面中传播和触发的过程,分为捕获阶段、目标阶段和冒泡阶段。在这个过程中,我们可以利用事件代理和阻止默认行为等技巧来更灵活地处理用户交互,事件代理(或称事件委托)是一种优化事件处理的方法。本文将深入探讨这些概念,帮助读者更好地理解和运用JavaScript中的事件流机制。

什么是JS的事件

  JavaScript事件是指用户与网页交互或浏览器发生特定情况时,可以触发执行特定代码的机制。这些交互包括但不限于用户点击按钮、键盘输入、鼠标移动等。事件是前端开发中实现交互和响应用户操作的基本方式。 以下是一些常见的JavaScript事件:

  1. 鼠标事件:

    • click: 单击鼠标触发。
    • mouseovermouseout: 鼠标移入和移出元素触发。
    • mousedownmouseupmousemove: 鼠标按下、释放和移动触发。
  2. 键盘事件:

    • keydownkeyupkeypress: 键盘按下、释放和按下并立即释放触发。等

事件流触发过程

  事件流分为捕获阶段、目标阶段和冒泡阶段。 案例1:有3个不同颜色的div容器,最外层红色盒子里放着蓝色的盒子,蓝色盒子里放着黄色盒子,盒子之间层层叠加。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #a{
      width: 400px;
      height: 400px;
      background-color: rgb(245, 56, 13);
    }
    #b{
      width: 200px;
      height: 200px;
      background-color: rgb(27, 140, 238);
    }
    #c{
      width: 100px;
      height: 100px;
      background: #000;
    }
  </style>
</head>
<body>
  <div id="a">
    <div id="b">
      <div id="c"></div>
    </div>
  </div>
  <script>
    let a = document.getElementById('a')
    let b = document.getElementById('b')
    let c = document.getElementById('c')
     a.addEventListener('click', () => {
       console.log('a被点击');
    })
     b.addEventListener('click', (event) => {
       console.log('b被点击');
    })
    c.addEventListener('click', (event) => {
      console.log('c被点击');
    }
  </script>
</body>
</html>

当我点击黄色方块时 打印的顺序结果为 c b a

  当我点击蓝色方块时 打印的顺序结果为 b a

这个结果为什么和我想得不一样呢 不着急 接下来我跟你讲讲清楚

捕获事件

捕获事件的流程包括事件从window对象向下传播到触发事件的目标元素的过程。以下是捕获事件的详细流程:

  1. 开始阶段: 事件开始于window对象,即全局对象。
  2. 捕获阶段开始: 事件在DOM树中向下传播,沿着父元素到子元素的路径。在这个过程中,如果某个祖先元素(父元素、祖父元素等)上有注册捕获事件的处理程序,这些处理程序将按照从外到内的顺序被触发。
  3. 目标元素: 事件传播到达目标元素,即触发事件的元素。在捕获阶段的最后一步,目标元素上注册的捕获事件处理程序将被触发。

  通俗易懂的就是指的是事件从DOM根节点 沿着DOM树往下 传递,直到到达目标元素的父元素,依次触发父元素上的事件处理程序。着重于从外向内传播。

上面的案例就是从winow--> html --> body --->div

冒泡阶段: 事件从目标元素开始向上冒泡到window对象。在这个过程中,如果目标元素或其祖先元素上有注册冒泡事件的处理程序,这些处理程序将按照从内到外的顺序被触发。

上面案例就是 div---> body----> html--->window

  指的是事件从目标元素 开始往上 冒泡,直到到达DOM根节点 ,依次触发所有父元素上的事件处理程序。着重于从内向外传播。

这个整体流程形成了事件的捕获阶段、目标阶段和冒泡阶段。

捕获事件与冒泡事件的互斥关系

  捕获事件和冒泡事件是互斥的,即同一个事件不能同时在捕获和冒泡阶段触发。

再来个案例修改js代码

javascript 复制代码
 a.addEventListener('click', function
            () {
            console.log('a被点击');
        },true)
  b.addEventListener('click', function
            () {
            console.log('b被点击');
        },true)
  c.addEventListener('click', event => {
            console.log('c被点击');},true)

  当我点击黄色方块时 打印的顺序结果为 a b c

  在实际应用中,可以通过addEventListener方法的第三个参数来控制是在捕获阶段还是冒泡阶段处理事件。若该参数为true,则在捕获阶段处理事件;若为false或省略,则在冒泡阶段处理事件。

阻止默认行为

  在处理事件时,有时我们需要阻止事件的默认行为,

  1. event.stopPropagation(): 该方法用于阻止事件继续传播,即停止事件在DOM树上的捕获或冒泡过程。
  2. event.stopImmediatePropagation(): 除了阻止事件传播外,该方法还会阻止元素上绑定的其他事件的执行。

  这两个方法的灵活运用可以有效地控制事件的传播和执行过程。

  第三个案例 修改js代码

javascript 复制代码
 a.addEventListener('click', function
            () {
            console.log('a被点击');


        })
        b.addEventListener('click', function
            () {
            console.log('b被点击');
            event.stopPropagation();
        })
        c.addEventListener('click', event => {
            console.log('c被点击');
        })

  当我们addEventListener方法的第三个参数为false是默认为冒泡传播是触发,则 event.stopPropagation()阻止冒泡转播 所有打印的结果为 c b

还有一种案例修改js代码

javascript 复制代码
 a.addEventListener('click', function
            () {
            console.log('a被点击');


        }, true)
        b.addEventListener('click', function
            () {
            console.log('b被点击');
            event.stopPropagation();
        })
        c.addEventListener('click', event => {
            console.log('c被点击');
        })

  当b的addEventListener方法的第三个参数为true是默认为冒泡传播是触发,则 event.stopPropagation()阻止捕获转播 所有打印的结果为 a b

事件代理(事件委托)

  事件代理是一种利用事件冒泡原理的技术,将事件绑定在父元素上,从而通过事件流触发子元素上的事件。这样做的好处在于:

  1. 减少内存占用:只需在父元素上绑定一个事件,而不是在每个子元素上都绑定事件,节省内存空间。
  2. 动态元素处理:对于动态添加的子元素,无需重新绑定事件,依然可以通过事件代理捕获并处理事件。

案例:

javascript 复制代码
 <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
    </ul>

    <!-- <script>
        // 点击哪一个li,就log出哪个内容
        var lis = document.getElementsByTagName('li');
        lis.__proto__.forEach=Array.prototype.forEach;
        Array.from(lis).forEach(function (li) {
            li.addEventListener('click', function () {
                console.log(this.innerText);
            })
        })   
    </script>

  这样写占用了很多内存,还要绑定很多事件,这样就很麻烦,我们可以用事件委托的方法 代码如下

xml 复制代码
<script>
        //   点击哪一个li,就log出哪个内容
        var ul = document.getElementsByTagName('ul')[0];
        ul.addEventListener('click', (event) => {
            // 此时事件流从哪里来到ul
            console.log(event.target.innerText);
        })
    </script>

  这个方法就用到了冒泡事件 当我们点击到li,就会冒泡到ul上 在用ul里面的target的属性来获取到li 让它输出其内容

结语

  深入理解JavaScript中的事件流触发过程以及相关概念,对于构建更灵活、高效的交互体验至关重要。通过合理使用捕获和冒泡阶段、阻止默认行为以及事件代理等技术,我们能够更好地处理用户的操作,提升网页的交互性和性能。希望本文能够帮助读者更深入地掌握这些关键概念。喜欢的来个关注 点赞 这个也是以后写文章的动力所在 谢谢大家能观看我的文章 咱下期再见 拜拜

相关推荐
To_OC19 分钟前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic27 分钟前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy33 分钟前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志1 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble2 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长2 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯3 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜4 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司4 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
心中有国也有家4 小时前
AtomGit Flutter 鸿蒙客户端:ModalBottomSheet 实战
android·javascript·学习·flutter·华为·harmonyos