link 简介
网页都是关于交互的。用户执行无数操作,例如将鼠标移到页面上、单击元素和在文本框中输入文本——所有这些都是事件的示例。除了这些用户事件之外,还发生许多其他事件,例如当页面加载时、当视频开始播放或暂停时等。每当页面上发生一些有趣的事情时,就会触发一个事件,这意味着浏览器基本上会宣布某事已发生。正是这个公告允许开发人员“监听”事件并适当地对它们做出反应。
link 什么是 DOM 事件?
如前所述,有无数的事件类型,但也许最容易理解的是用户事件,例如当有人单击元素或在表单中输入内容时。这些类型的事件发生在元素上,这意味着当用户单击按钮时,例如,按钮上发生了事件。虽然用户交互并不是 DOM 事件的唯一类型,但它们肯定是最容易理解的,特别是刚开始的时候。Mozilla Developer Network 对可用的 DOM 事件进行了很好的参考。
link 监听事件的方法
有很多方法可以监听事件。网页上不断发生操作,但只有当开发人员监听它们时,才会收到它们的通知。监听事件基本上意味着您正在等待浏览器告诉您已发生特定事件,然后您将指定页面应如何做出反应。
要向浏览器指定在事件发生时要做什么,您需要提供一个函数,也称为事件处理程序。每当事件发生(或直到事件解除绑定)时,都会执行此函数。
例如,要每当用户单击按钮时发出警报,你可以写类似这样的内容
1
|
|
我们要监听的事件由按钮的onclick
属性指定,事件处理程序是alert
函数,它向用户发出“Hello”警报。虽然这有效,但由于以下几个原因,这是实现此功能的一种糟糕方法
- 首先,我们将视图代码(HTML)与交互代码(JS)耦合在一起。这意味着每当我们需要更新功能时,我们都必须编辑 HTML,这是一种不好的做法,也是维护的噩梦。
- 其次,它不可扩展。如果你必须将此功能附加到多个按钮上,你不仅会用一堆重复的代码使页面臃肿,而且还会再次破坏可维护性。
像这样使用内联事件处理程序可以被认为是侵入式 JavaScript,但它的反面,非侵入式 JavaScript是讨论该主题更常见的方式。非侵入式 JavaScript的概念是你的 HTML 和 JS 是分开的,因此更易于维护。关注点分离很重要,因为它将类似的代码片段(即 HTML、JS、CSS)放在一起,并将不同的代码片段分开,从而方便更改、增强等。此外,非侵入式 JavaScript 强调尽可能减少向页面添加的冗余代码。如果用户的浏览器不支持 JavaScript,那么它不应该与页面的标记交织在一起。此外,为了防止命名冲突,JS 代码应为不同的功能或库使用单个命名空间。jQuery 就是一个很好的例子,因为jQuery
对象/构造函数(以及$
别名到jQuery
)只使用一个全局变量,并且 jQuery 的所有功能都打包到该一个对象中。
为了以非侵入式的方式完成所需的任务,让我们通过删除onclick
属性并用一个id
替换它来稍微更改我们的 HTML,我们将在脚本文件中使用该id
“挂钩”到按钮。
1
|
|
如果我们希望在用户以非侵入式方式单击该按钮时收到通知,我们可以在单独的脚本文件中执行类似以下操作
1
2
3
4
5
6
|
|
在这里,我们通过调用 getElementById
并将其返回值分配给变量来保存对按钮元素的引用。然后,我们调用 addEventListener
并提供一个事件处理程序函数,该函数将在该事件发生时被调用。虽然这段代码没有问题,因为它在现代浏览器中可以正常工作,但在 IE9 之前的 IE 版本中却无法正常工作。这是因为 Microsoft 选择实现不同的方法 attachEvent
,而不是 W3C 标准 addEventListener
,并且直到 IE9 发布后才改变了它。因此,利用 jQuery 是有益的,因为它消除了浏览器的不一致性,允许开发人员使用单个 API 来执行这些类型的任务,如下所示。
1
2
3
4
|
|
$( "#helloBtn" )
代码使用 $
(又名 jQuery
)函数选择按钮元素并返回 jQuery 对象。jQuery 对象有很多可用的方法(函数),其中一个名为 click
,它位于 jQuery 对象的原型中。我们在 jQuery 对象上调用 click
方法并传递一个匿名函数事件处理程序,当用户单击按钮时该处理程序将被执行,向用户发出“Hello”。
可以使用 jQuery 侦听事件的方法有很多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
|
从 jQuery 1.7 开始,所有事件都通过 on
方法绑定,无论您直接调用它还是使用别名/快捷方式方法(例如 bind
或 click
),这些方法在内部都映射到 on
方法。考虑到这一点,使用 on
方法是有益的,因为其他方法都只是语法糖,而使用 on
方法将产生更快、更一致的代码。
让我们看看上面的 on
示例并讨论它们的差异。在第一个示例中,将字符串 click
作为第一个参数传递给 on
方法,并将匿名函数作为第二个参数传递。这看起来很像之前的 bind
方法。在这里,我们将事件处理程序直接附加到 #helloBtn
。如果页面上还有其他按钮,当单击这些按钮时,它们不会发出“Hello”提示,因为该事件仅附加到 #helloBtn
。
在第二个 on
示例中,我们传递了一个对象(由大括号 {}
表示),它有一个属性 click
,其值是一个匿名函数。on
方法的第二个参数是一个 jQuery 选择器字符串 button
。虽然示例 1-3 在功能上是等效的,但示例 4 不同,因为 body
元素正在侦听发生在任何按钮元素上的单击事件,而不仅仅是 #helloBtn
。上面的最后一个示例与前一个示例完全相同,但我们传递的是一个事件字符串、一个选择器字符串和回调,而不是传递一个对象。这两个示例都是事件委托的示例,事件委托是一个元素在 DOM 树中侦听其子元素上发生的事件的过程。
link 事件委托
事件委托之所以有效,是因为事件冒泡的概念。对于大多数事件,每当页面上发生某些事情(例如单击某个元素)时,事件就会从发生事件的元素传播到其父元素,然后传播到父元素的父元素,依此类推,直到到达根元素,即 window
。因此,在我们的表格示例中,每当单击 td
时,其父元素 tr
也会收到单击通知,父元素 table
会收到通知,body
会收到通知,最终 window
也会收到通知。虽然事件冒泡和委托运行良好,但委托元素(在我们的示例中,即 table
)应始终尽可能靠近被委托元素,以便事件不必在调用其处理程序函数之前在 DOM 树中向上传播很远。
事件委托相对于直接绑定到元素(或一组元素)的两个主要优点是性能和前面提到的事件冒泡。想象一下有一个包含 1000 个单元格的大表格,并为每个单元格绑定一个事件。这是 1000 个单独的事件处理程序,浏览器必须附加这些处理程序,即使它们都映射到同一个函数。但是,我们不必绑定到每个单独的单元格,而是可以使用委托来侦听发生在父表格上的事件并做出相应反应。将绑定一个事件而不是 1000 个事件,从而大大提高性能和内存管理。
发生的事件冒泡使我们能够通过 Ajax 添加单元格,例如,而不必将事件直接绑定到这些单元格,因为父表正在侦听点击并因此收到其子项上的点击通知。如果我们不使用委托,我们将不得不为每个添加的单元格不断绑定事件,这不仅是一个性能问题,而且还可能成为维护噩梦。
链接 事件对象
在所有先前的示例中,我们一直在使用匿名函数并在该函数中指定一个 event
参数。让我们稍作更改。
1
2
3
4
5
6
|
|
在这个稍有不同的示例中,我们正在定义一个名为 sayHello
的函数,然后将该函数传递到 on
方法中,而不是匿名函数。如此多的在线示例显示了用作事件处理程序的匿名函数,但重要的是要认识到,您还可以将已定义的函数作为事件处理程序传递。如果不同的元素或不同的事件应执行相同的功能,这一点很重要。这有助于保持代码 DRY。
但是 sayHello
函数中的 event
参数是什么——它是什么,为什么它很重要?在所有 DOM 事件回调中,jQuery 传递一个 事件对象 参数,其中包含有关事件的信息,例如它发生的准确时间和地点、它是什么类型的事件、事件发生在哪个元素上以及大量其他信息。当然,您不必称它为 event
;您可以称它为 e
或您想要的任何名称,但 event
是一个非常常见的惯例。
如果元素具有特定事件的默认功能(例如,链接打开新页面,表单中的按钮提交表单等),则可以取消该默认功能。这通常对 Ajax 请求很有用。当用户单击按钮通过 Ajax 提交表单时,我们希望取消按钮/表单的默认操作(将其提交到表单的 action
属性),而我们改为执行 Ajax 请求以完成同一任务,以获得更无缝的体验。为此,我们将利用事件对象并调用其 .preventDefault()
方法。我们还可以使用 .stopPropagation()
阻止事件在 DOM 树中冒泡,以便父元素不会收到其发生通知(在使用事件委托的情况下)。
1
2
3
4
5
6
7
8
9
10
11
|
|
同时使用 .preventDefault()
和 .stopPropagation()
时,您可以改为 return false
以更简洁的方式实现两者,但建议仅在两者都实际需要时才 return false
,而不仅仅是为了简洁。关于 .stopPropagation()
的最后一点说明是,在委托事件中使用它时,最早可以停止事件冒泡的时间是事件到达委托它的元素时。
同样重要的是要注意,事件对象包含一个名为 originalEvent
的属性,它是浏览器本身创建的事件对象。jQuery 用一些有用的方法和属性包装了这个本机事件对象,但在某些情况下,您需要通过 event.originalEvent
访问原始事件。这对于移动设备和平板电脑上的触摸事件特别有用。
最后,要检查事件本身并查看它包含的所有数据,您应该使用 console.log
在浏览器的控制台中记录事件。这将允许您查看事件的所有属性(包括 originalEvent
),这对于调试非常有帮助。
1
2
3
4
5
6
7
8
9
10
11
|
|