动态 Web 交互的隐藏关键

当我还在学习 Web 开发的诀窍时,我遇到的令人惊讶且有趣的行为之一就是事件冒泡。 起初,这看起来很不寻常,但是当您考虑事件冒泡时,您会发现它非常有意义。 作为一名 Web 开发人员,您必然会遇到事件冒泡。 那么什么是事件冒泡呢?

为了向用户提供与网页交互的能力,JavaScript 依赖于事件。 事件是指您编写的代码可以检测到并响应的事件或操作。 事件的示例包括鼠标单击、按键和表单提交等。

JavaScript 使用事件侦听器来检测和响应事件。 事件监听器是监听或等待页面上发生的特定事件的函数。 例如,按钮上的单击事件。 当事件侦听器检测到它正在侦听的事件时,它会通过执行与该事件关联的代码来响应。 捕获和响应事件的整个过程称为事件处理。

现在,假设页面上有三个元素:div、span 和按钮。 Button 元素嵌套在span 元素内,span 元素嵌套在div 内。 下图展示了这一点:

假设每个元素都有一个事件侦听器,用于侦听单击事件并在单击时打印到控制台,那么当您单击按钮时会发生什么?

要自行测试,请创建一个文件夹,并在该文件夹中创建一个名为 index.html 的 HTML 文件、一个名为 style.css 的 CSS 文件和一个名为 app.js 的 JavaScript 文件。

在 HTML 文件中,添加以下代码:

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

<head>
  <title>Event bubbling</title>
  <link rel="stylesheet" href="https://wilku.top/the-hidden-key-to-dynamic-web-interactions/style.css">
</head>

<body>
  <div>
    <span><button>Click Me!</button></span>
  </div>
  <script src="app.js"></script>
</body>

</html>

在 CSS 文件中,添加以下代码来设置 div 和 span 元素的样式。

div {
  border: 2px solid black;
  background-color: orange;
  padding: 30px;
  width: 400px;
}

span {
  display: inline-block;
  background-color: cyan;
  height: 100px;
  width: 200px;
  margin: 10px;
  padding: 20px;
  border: 2px solid black;
}

在 JavaScript 文件中,添加以下代码,将事件侦听器添加到 div、span 和 button 元素。 事件列表器都在监听单击事件。

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

const span = document.querySelector('span');
span.addEventListener('click', () => {
  console.log("You've clicked a span element")
})

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button")
})

现在在浏览器中打开 HTML 文件。 检查页面,然后单击页面上的按钮。 你注意到了什么? 点击按钮的输出如下所示:

  如何从 iCloud 中删除消息

单击按钮会触发事件侦听器侦听按钮上的单击事件。 但是,span 和 div 元素上的事件侦听器也会被触发。 您可能会问,为什么会出现这种情况。

单击按钮会触发附加到按钮的事件侦听器,该事件侦听器会打印到控制台。 但是,由于按钮嵌套在 span 元素内,因此单击按钮意味着从技术上讲,我们也在单击 span 元素,因此会触发其事件侦听器。

由于span元素也嵌套在div元素内部,所以点击span元素也意味着div元素也被点击,结果,它的事件监听器也被触发。 这就是事件冒泡。

事件冒泡

事件冒泡是这样的过程:在嵌套的 HTML 元素集中触发的事件被传播或从触发它的最里面的元素向上冒泡,并沿 DOM 树向上传播到根元素,从而触发侦听该事件的所有事件侦听器。

事件侦听器按照特定的顺序触发,该顺序与事件在 DOM 树中冒泡或传播的方式相匹配。 考虑下面显示的 DOM 树,它表示本文中使用的 HTML 结构。

单击事件在 DOM 树中冒泡

DOM 树显示了一个按钮,嵌套在 span 内,span 嵌套在 div 内,div 嵌套在 body 内,而 body 又嵌套在 HTML 元素内。 由于单击按钮时元素已相互嵌套,因此单击事件将触发附加到按钮的事件侦听器。

然而,由于元素是嵌套的,事件将沿着 DOM 树(向上冒泡)向上移动到 span 元素,然后是 div,然后是 body,最后是 HTML 元素,从而触发监听该点击事件的所有事件监听器。命令。

这就是执行附加到 span 和 div 元素的事件侦听器的原因。 如果我们有事件监听器监听 body 和 HTML 元素上的点击,它们也会被触发。

事件发生的 DOM 节点称为目标。 在我们的例子中,由于单击发生在按钮上,因此按钮元素是事件目标。

如何停止事件冒泡

为了阻止事件在 DOM 中冒泡,我们使用事件对象上可用的名为 stopPropagation() 的方法。 考虑下面的代码示例,我们用它来向按钮元素添加事件侦听器。

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
})

当用户单击按钮时,该代码会导致事件在 DOM 树中冒泡。 为了防止事件冒泡,我们调用 stopPropagation() 方法,如下所示:

const button = document.querySelector('button');
button.addEventListener('click', (e) => {
  console.log("You've clicked a button");
  e.stopPropagation();
})

事件处理程序是单击按钮时执行的函数。 事件侦听器自动将事件对象传递给事件处理程序。 在我们的例子中,该事件对象由变量名 e 表示,该变量在事件处理程序中作为参数传递。

  如何使用 Google Drive 渐进式网页应用

该事件对象包含有关事件的信息,并且还使我们能够访问与事件相关的各种属性和方法。 其中一种方法是 stopPropagation(),它用于防止事件冒泡。 在按钮的事件侦听器中调用 stopPropagation() 可防止事件从按钮元素向上冒泡到 DOM 树。

添加stopPropagation()方法后点击按钮的结果如下所示:

我们使用 stopPropagation() 来防止事件从我们使用它的元素中冒泡。 例如,如果我们希望单击事件从按钮元素向上冒泡到 span 元素,而不是在 DOM 树中进一步向上冒泡,我们可以在 span 的事件侦听器上使用 stopPropagation()。

事件捕捉

事件捕获与事件冒泡相反。 在事件捕获中,事件从最外层元素向下渗透到目标元素,如下所示:

由于事件捕获,点击事件逐渐向下传递到目标元素

例如,在我们的例子中,当您单击按钮元素时,在事件捕获中,div 元素上的事件侦听器将首先被触发。 span 元素上的监听器紧随其后,最后,目标元素上的监听器将被触发。

然而,事件冒泡是事件在文档对象模型 (DOM) 中传播的默认方式。 要将默认行为从事件冒泡更改为事件捕获,我们将第三个参数传递给事件侦听器以将事件捕获设置为 true。 如果您不将第三个参数传递给事件侦听器,则事件捕获将设置为 false。

考虑下面的事件监听器:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

由于它没有第三个参数,因此 capture 设置为 false。 要将捕获设置为 true,我们传递第三个参数,即布尔值 true,它将捕获设置为 true。

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

或者,您可以传入一个将 capture 设置为 true 的对象,如下所示:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, {capture: true})

要测试事件捕获,请在 JavaScript 文件中向所有事件侦听器添加第三个参数,如下所示:

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

const span = document.querySelector('span');
span.addEventListener('click', (e) => {
  console.log("You've clicked a span element")
}, true)

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
}, true)

现在打开浏览器并单击按钮元素。 你应该得到这样的输出:

  可以为您节省数百万美元的 8 种最佳数据丢失防护解决方案

请注意,与事件冒泡中首先打印出按钮的输出不同,在事件捕获中,第一个输出来自最外层元素 div。

事件冒泡和事件捕获是事件在 DOM 中传播的主要方式。 然而,事件冒泡通常用于传播事件。

活动委托

事件委托是一种设计模式,其中单个事件侦听器附加到公共父元素(例如

    元素),而不是在每个子元素上都有事件侦听器。 然后,子元素上的事件向上冒泡到父元素,并由父元素上的事件处理程序进行处理。

    因此,如果我们有一个父元素,其中包含子元素,我们只为父元素添加一个事件监听器。 该事件处理程序将处理子元素中的所有事件。

    您可能想知道,父元素如何知道单击了哪个子元素。 如前所述,事件侦听器将事件对象传递给事件处理程序。 该事件对象具有提供有关特定事件的信息的方法和属性。 事件对象中的属性之一是目标属性。 target 属性指向发生事件的特定 HTML 元素。

    例如,如果我们有一个包含列表项的无序列表,并且当列表项上发生事件时,我们将事件侦听器附加到

      元素,则事件对象中的 target 属性将指向事件所在的特定列表项发生。

      要查看事件委托的实际效果,请将以下 HTML 代码添加到现有 HTML 文件中:

      <ul>
          <li>Toyota</li>
          <li>Subaru</li>
          <li>Honda</li>
          <li>Hyundai</li>
          <li>Chevrolet</li>
          <li>Kia</li>
        </ul>

      添加以下 JavaScript 代码以使用事件委托来使用父元素上的单个事件侦听器来侦听子元素中的事件:

      const ul = document.querySelector('ul');
      ul.addEventListener('click', (e) => {
        // target element
        targetElement = e.target
        // log out the content of the target element
        console.log(targetElement.textContent)
      })

      现在打开浏览器并单击列表中的任意项目。 元素的内容应打印到控制台,如下所示:

      使用单个事件侦听器,我们可以处理所有子元素中的事件。 页面上有如此多的事件侦听器会影响其性能,消耗更多内存,并导致页面加载和渲染缓慢。

      事件委托允许我们通过最小化需要在页面上使用的事件侦听器的数量来避免这一切。 事件委托依赖于事件冒泡。 因此,我们可以说事件冒泡有助于优化网页的性能。

      高效事件处理的技巧

      作为开发人员,在处理文档对象模型中的事件时,请考虑使用事件委托,而不是在页面上的元素上使用大量事件侦听器。

      使用事件委托时,请记住将事件侦听器附加到需要事件处理的子元素的最近公共祖先。 这有助于优化事件冒泡,并最大限度地减少事件在处理之前必须经过的路径。

      处理事件时,使用事件侦听器提供的事件对象对您有利。 事件对象包含目标等属性,这些属性在处理事件时派上用场。

      要拥有更高性能的网站,请避免过度的 DOM 操作。 触发频繁 DOM 操作的事件可能会对网站的性能产生负面影响。

      最后,对于嵌套元素,将嵌套事件侦听器附加到元素时要非常小心。 这不仅会影响性能,还会使事件处理变得非常复杂,并且您的代码将难以维护。

      结论

      事件是 JavaScript 中的一个强大工具。 事件冒泡、事件捕获和事件委托是 JavaScript 中处理事件的重要工具。 作为 Web 开发人员,请使用本文来熟悉概念,以便您可以构建更具交互性、动态性和高性能的网站和应用程序。