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

相关推荐
东东5164 分钟前
OA自动化居家办公管理系统 ssm+vue
java·前端·vue.js·后端·毕业设计·毕设
周某人姓周8 分钟前
DOM型XSS案例
前端·安全·web安全·网络安全·xss
程序员鱼皮19 分钟前
前特斯拉 AI 总监:AI 编程最大的谎言,是 “提效”
前端·后端·ai·程序员·开发
pusheng202531 分钟前
普晟传感2026年新春年会总结与分析
前端·javascript·html
谢尔登32 分钟前
React19事件调度的设计思路
前端·javascript·react.js
Emma_Maria42 分钟前
本地项目html和jquery,访问地址报跨域解决
前端·html·jquery
奋斗吧程序媛1 小时前
常用且好用的命令
前端·编辑器
2301_796512521 小时前
【精通篇】打造React Native鸿蒙跨平台开发高级复合组件库开发系列:Lazyload 懒加载(懒加载的图片)
前端·javascript·react native·react.js·ecmascript·harmonyos
敲敲了个代码1 小时前
从N倍人力到1次修改:Vite Plugin Modular 如何拯救多产品前端维护困境
前端·javascript·面试·职场和发展·typescript·vite
摘星编程1 小时前
OpenHarmony环境下React Native:Timeline时间轴组件
javascript·react native·react.js