《深入探秘 JavaScript 事件流、事件委托与事件解绑,提升前端开发效率》

145 阅读4分钟

一、事件流

  1. 捕获

    • 描述:
      在 JavaScript 中,事件捕获是从 DOM 树的根节点开始,向目标元素依次传递事件的过程。例如,假设我们有以下 HTML 结构:

     <div id="outer">
       <div id="inner">
         <button id="btn">点击我</button>
       </div>
     </div>

如果我们在window对象上添加一个click事件监听器,并设置useCapture参数为true,当点击#btn按钮时,事件首先会在window上触发(假设没有其他更外层元素),然后依次传递到#outer#inner,最后到达#btn

     window.addEventListener('click', function() {
       console.log('window捕获');
     }, true);
     document.getElementById('outer').addEventListener('click', function() {
       console.log('outer捕获');
     }, true);
     document.getElementById('inner').addEventListener('click', function() {
       console.log('inner捕获');
     }, true);
     document.getElementById('btn').addEventListener('click', function() {
       console.log('btn捕获');
     }, true);
  1. 事件冒泡

    • 描述:
      事件冒泡是从目标元素开始,沿着 DOM 树向上传播事件。以刚才的 HTML 结构为例,当我们给#btn添加一个click事件监听器(不设置useCapture,默认为false,即冒泡阶段处理),然后点击#btn,事件首先在#btn上触发,接着冒泡到#inner#outer,如果有window上的冒泡阶段的click事件监听器,也会触发。

     document.getElementById('btn').addEventListener('click', function() {
       console.log('btn冒泡');
     });
     document.getElementById('inner').addEventListener('click', function() {
       console.log('inner冒泡');
     });
     document.getElementById('outer').addEventListener('click', function() {
       console.log('outer冒泡');
     });
     window.addEventListener('click', function() {
       console.log('window冒泡');
     });
  1. 阻止冒泡

    • 描述:
      在事件处理函数中,可以使用event.stopPropagation()方法来阻止事件冒泡。例如,我们不希望#btnclick事件冒泡到#inner,可以这样做:

     document.getElementById('btn').addEventListener('click', function(event) {
       console.log('btn点击,阻止冒泡');
       event.stopPropagation();
     });

二、事件委托

  1. 得到子元素

    • 描述:
      假设我们有一个<ul>列表,里面有多个<li>元素。我们将click事件绑定到<ul>上,在事件处理函数中通过event.target获取实际被点击的<li>元素。

     <ul id="myList">
       <li>列表项1</li>
       <li>列表项2</li>
       <li>列表项3</li>
     </ul>
     document.getElementById('myList').addEventListener('click', function(event) {
       if (event.target.tagName === 'LI') {
         console.log('点击了列表项: ', event.target.textContent);
       }
     });
  1. 事件委托案例练习

    • 练习内容:
      有一个<ul>列表,里面有若干个<li>元素,每个<li>元素都有一个不同颜色的背景(通过内联样式设置)。要求使用事件委托,当点击某个<li>时,将其背景颜色设置为红色。

    • 解答:

     <ul id="colorList">
       <li style="background - color: blue;">蓝色背景项</li>
       <li style="background - color: green;">绿色背景项</li>
       <li style="background - color: yellow;">黄色背景项</li>
     </ul>
     document.getElementById('colorList').addEventListener('click', function(event) {
       if (event.target.tagName === 'LI') {
         event.target.style.backgroundColor = 'red';
       }
     });

三、阻止默认行为

  1. 描述

    • 在 JavaScript 中,许多 HTML 元素都有默认行为。例如,<a>标签默认会跳转到其href属性指定的链接。如果我们想阻止这种默认行为,可以在<a>标签的click事件处理函数中使用event.preventDefault()

     <a href="https://www.example.com" id="myLink">示例链接</a>
     document.getElementById('myLink').addEventListener('click', function(event) {
       console.log('点击了链接,但阻止跳转');
       event.preventDefault();
     });

四、事件解绑

  1. 案例练习

    • 练习内容:
      有一个按钮<button id="myButton">点击我</button>,当页面加载时,给按钮添加一个click事件处理函数,该函数在按钮被点击时在控制台输出一条消息。当按钮被点击两次后,解除该按钮的click事件处理函数。

    • 解答:

     <button id="myButton">点击我</button>
     let clickCount = 0;
     const button = document.getElementById('myButton');
     const clickHandler = function() {
       console.log('按钮被点击了');
       clickCount++;
       if (clickCount === 2) {
         button.removeEventListener('click', clickHandler);
       }
     };
     button.addEventListener('click', clickHandler);

五、其他事件

  1. 页面加载事件

    • 事件名:load

      • 描述:
        load事件在整个页面(包括所有资源,如图片、样式表等)都加载完成后触发。例如,我们有一个页面包含一张图片,当图片和页面所有内容都加载完后执行一个函数。

       <img src="example.jpg" alt="示例图片">
       window.addEventListener('load', function() {
         console.log('页面和所有资源加载完成');
       });
  • 事件名:DOMContentLoaded

    • 描述:
      DOMContentLoaded事件在 HTML 文档被完全解析和加载(DOM 树构建完成)后触发,但不等待样式表、图片等外部资源加载完成。

       <script>
         document.addEventListener('DOMContentLoaded', function() {
           console.log('DOM树构建完成');
         });
       </script>

六、元素滚动事件

  1. 案例练习

    • 案例内容:
      有一个长页面,页面中有一个固定位置的导航栏,当页面滚动到一定位置时,希望导航栏的背景颜色从透明变为不透明,以增强可读性。

    • 分析:
      首先,需要监听页面的滚动事件,可以使用window.addEventListener('scroll', function() {...})。在滚动事件处理函数中,获取页面的滚动距离(可以通过window.scrollY获取)。当滚动距离达到某个阈值(例如 200px)时,改变导航栏的背景颜色。

     <nav id="myNav">导航栏</nav>
     <div style="height:1000px;">一些内容,用于滚动</div>
     const navBar = document.getElementById('myNav');
     window.addEventListener('scroll', function() {
       if (window.scrollY > 200) {
         navBar.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
       } else {
         navBar.style.backgroundColor = 'transparent';
       }
     });

七、页面尺寸事件

  1. 描述

    • 页面尺寸事件主要用于处理浏览器窗口大小改变的情况。在 JavaScript 中,可以使用window.addEventListener('resize', function() {...})来监听窗口大小的改变。例如,当窗口大小改变时,我们可以调整页面中某个元素的大小。假设我们有一个<div>元素,我们希望它的宽度始终是窗口宽度的一半。

     <div id="myDiv" style="height:100px;"></div>
     const div = document.getElementById('myDiv');
     window.addEventListener('resize', function() {
       div.style.width = window.innerWidth / 2 + 'px';
     });

八、元素尺寸与位置

  1. 案例练习

    • 练习内容:
      有一个<div>元素,在页面加载完成后,计算并在控制台输出该<div>元素的宽度、高度、相对于页面左上角的水平位置和垂直位置。

    • 解答:

     <div id="myDiv" style="width:200px; height:150px; margin - left:50px; margin - top:30px;"></div>
     window.addEventListener('load', function() {
       const div = document.getElementById('myDiv');
       console.log('宽度: ', div.offsetWidth);
       console.log('高度: ', div.offsetHeight);
       console.log('水平位置: ', div.offsetLeft);
       console.log('垂直位置: ', div.offsetTop);
     });