关于事件的那些事

65 阅读6分钟

大家好,我是森木。本文我们将从基础出发彻彻底底理解什么是“事件”,以及它的一些常用场景。下面是此次学习的清单 🧾:

  • 什么是事件 🌟
  • 事件的核心模块🌟🌟
    • 事件流 🌟🌟
    • 事件处理程序 🌟
    • 事件对象 🌟
    • 事件类型 🌟🌟
    • 内存和性能 🌟🌟
    • 模拟事件 🌟🌟🌟
  • 阻止事件传播 🌟🌟

🌟 表示基础必学,如果你是老手可以跳过,1 min

🌟🌟 表示入门, 2-3 min

🌟🌟🌟 表示进阶,2 min

大家可以根据自己的基础水平选择性阅读哦~好啦,废话不多说,直接开始我们的航行 🚢💨💨💨

什么是事件

概览

事件是在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,可以对事件做出回应。JavaScript 与 HTML 之间的交互就是通过事件来实现的。在 Web 中,浏览器的窗口变化、页面中某个/些元素的变化等都是事件,如果想知道更多事件请参考:。而在它们交互的瞬间,可以使用侦听器(或者处理程序)来预订事件,以便事件发生时执行相应的代码。这种模式我相信大家肯定不陌生,就是观察者模式。使用这种方式可以很好的将页面的行为(JavaScript)和页面的外观(HTML 和 CSS)进行解耦。

历史

DOM(Document Object Model)文档对象模型

事件最早是在 IE3 和 Netscape Navigator 2 中出现的,当时是作为分担服务器运算负载的一种手段。到了 IE4 和 Navigator 4 发布时,它们都提供了相似但不相同的 API,这些 API 并存经过了好几个主要版本。考虑到不同浏览器可能实现不同的 API ,之后 DOM2 级规范便开始尝试以一种符合逻辑的方式来标准化 DOM 事件。值得一提的是,IE9 还有我们熟知的一些浏览器都已经实现了“DOM2 级事件”模块的核心部分,但是 IE8 仍然在使用其专有的事件系统。

小结

image.png 并不是只有 JavaScript 使用事件,大多的编程语言都有这种机制,它们的工作方式不同于 JavaScript。实际上,JavaScript 网页上的事件机制不同于在其他环境中的事件机制。

事件的核心模块

事件流

提到事件流相信很多人并不陌生,我们这里把它分为三部分:IE 事件流、Netscape 事件流、DOM 事件流。 这里就简单将前两种分别说下:IE 事件流叫做事件冒泡,而 Netscape 事件流叫做事件捕获。对于 DOM 事件流我们详细说明:DOM2 级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事事件捕获,为截取事件提供了机会;其次是实际的目标收到了事件;最后是冒泡阶段,可以在这个阶段对事件作出响应。下面用一个简单的 HTML 页面为例:

<html>
  <head>
    <title>事件流</title>
  </head>
  <body>
    <div id="myDiv">Click Me</div>
  </body>
</html>

图示: image.png

根据上图,可以很清晰的看到,实际的目标( <div> 元素)在捕获阶段不会接收到事件。这就意味着在捕获阶段,事件从 document 到 <html> 再到 <body> 后就停止了。下一个阶段是“处于目标”阶段,于是事件就在 <div> 上发生了,并在事件处理中被看成事件冒泡阶段的一部分。最后,事件冒泡阶段发生,事件传播回文档。

事件处理程序

事件处理程序就是在响应某个事件的函数。分为:HTML 事件处理程序、DOM0 级事件处理程序、DOM2 级事件处理程序、IE 事件处理程序、跨浏览器事件处理程序。

事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象包含着所有与事件相关的信息。

事件类型

事件类型也好理解,例如:焦点事件、鼠标与滚轮事件、键盘与文本事件、HTML5 事件、触摸与手势事件、设备事件等等

内存与性能

想象这样一个场景:有一个列表都有一个删除按钮需要绑定事件与事件处理程序,如果我们还按照之前那种一个元素绑定一个事件来处理是可以,但是就面临了内存和性能问题,而且也是程序设计上的大忌。所以这是就出现了另一个方法:事件委托。意思就是可以将这些子元素的事件委托给父元素去完成,在父元素上监听事件处理响应程序,极大的减少了内存消耗并提升了性能。

每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的 JavaScript 代码之间就会建立一个连接。这种连接越多,页面执行就会越慢。如前所述,我们可以采用事件委托技术,限制连接的数量。另外,我们还可以在不需要的时候移除这些事件处理程序。一般在做性能优化时,我们可以同时采用这两种方法来。

模拟事件

通常,事件由用户操作或者通过其他浏览器功能来触发;但其实可以使用 JavaScript 在任意时刻出发特定的事件,而此时的事件就如同浏览器创建的事件一样。也就是说,这些事件该冒泡还会冒泡,而且照样能够让浏览器执行已经指定的处理它们的事件处理程序。这里我们不做详细的教程去讲解,大家有个概念即可,如果感兴趣的话可以自己去查一些资料自己动手实现~

阻止事件传播

从上面事件流我们知道事件会有传播的特性,但是这种特性有时会给我们带来很多烦恼,这时我们就需要知道如何阻止事件传播,方法如下:

  • preventDefault() 阻止浏览器默认行为,例如:表单的提交
  • stopPropagation() 阻止捕获或者冒泡阶段的事件继续传播
  • stopImmediatePropagation() 阻止监听同一事件的其他事件监听器被调用
    • 简单来说就是如果某个事件被多处地方设置事件监听器去处理,它们会按照执行顺序来执行,如果当前事件设置了该方法,则之后同一事件监听器则不会被执行

总结

事件并不是 JavaScript 的核心部分——它们是在浏览器 Web API 中定义的。 另外,理解 JavaScript 在不同环境下使用不同的事件模型很重要——从 Web API 到其他领域,如浏览器 WebExtensions 和 Node.js。最后,在使用事件的时候需要注意内存与性能方面的问题