jQuery 提供了几种扩展其事件系统的方法,以便在将事件附加到元素时提供自定义功能。在 jQuery 内部,这些扩展主要用于确保标准事件(如 submit
和 change
)在不同浏览器中表现一致。但是,它们也可以用于定义具有自定义行为的新事件。
本文档涵盖了从 jQuery 1.7 开始提供的扩展;自 jQuery 1.3 以来,此功能的一个文档较少的子集已经可用,但功能差异很大。有关早期版本中特殊事件的概述,请参阅 Ben Alman 的 jQuery 特殊事件 文章。
link 事件概述和一般建议
在编写事件扩展时,了解事件通过 jQuery 内部事件系统流动的过程至关重要。有关 API 级别事件系统的说明,包括事件委托的讨论,请参阅 .on()
方法。
为了简化事件管理,jQuery 仅为每个元素的每个事件类型附加一个事件处理程序(在符合 W3C 的浏览器上使用 addEventListener
或在较旧的 IE 上使用 attachEvent
),然后分派到通过 jQuery 的 API 附加的事件处理程序。例如,如果三个“click”事件处理程序附加到一个元素,则当第一个处理程序附加时,jQuery 会附加它自己的处理程序,并将用户的事件处理程序添加到一个列表中,以便在事件发生时执行。对于后续的事件处理程序,jQuery 仅将它们添加到它自己的内部列表中,因为它已经调用浏览器来附加其独立的处理程序。相反,当某个特定类型的最后一个事件从元素中移除时,jQuery 会从浏览器中移除它自己的事件处理程序。
事件可以是 W3C 定义的原生事件,并且由浏览器触发,以响应用户单击鼠标按钮或按某个键等操作。它也可以是自定义事件,仅由代码通过 jQuery 的 .trigger()
或 .triggerHandler()
方法触发。代码还可以触发原生浏览器事件,这对于模拟用户操作非常方便。
通常,jQuery 并不内在知道浏览器是否可能触发事件名称。因此,默认情况下,当进行 API 调用以添加该事件的事件处理程序时,jQuery 始终将事件附加到浏览器。如果浏览器从未生成该事件类型,则处理程序被调用的唯一方式是 JavaScript 代码触发该事件。尽管将未使用的事件名称附加到浏览器通常没有危害,但可以使用特殊事件 setup
钩子来覆盖默认行为,如下所述。
每当通过 jQuery 从文档中移除元素时,事件系统都会尝试确保从元素中移除事件和相关数据,以防止内存泄漏。(如果不仔细管理,较旧版本的 Internet Explorer 在这些情况下会因内存泄漏而臭名昭著。)如果事件扩展附加事件或创建新对象,则它应该在通过定义 remove
和 teardown
钩子移除事件时分离这些对象或清除数据。
jQuery 事件扩展开发人员应避免使用在 DOM 设置中具有特殊含义的事件名称。诸如“click”、“change”或“load”之类的事件名称具有 W3C 定义的特定语义,因此将它们用作自定义事件可能会导致意外行为。通常,jQuery 事件扩展仅应在扩展在浏览器之间规范行为时用于 W3C 定义的事件名称。避免自定义事件冲突的常见约定是在事件类型名称中嵌入冒号或破折号,因为没有 W3C 事件使用这些字符。
尽管 jQuery 的事件系统面向于将 DOM 事件传递到 DOM 元素,但可以将 jQuery 方法用于在普通对象上附加和触发事件。例如,它可以用作简单的发布/订阅机制。事件扩展的开发人员应尝试避免在混合场景中使用 DOM 和普通对象时出现意外行为。jQuery 检测 DOM 元素的规范方法是在对象上检查 elem.nodeType === 1
。
链接 jQuery.event.props:数组
jQuery 定义了 事件对象,它表示事件发生时可用的信息的跨浏览器子集。jQuery.event.props
属性是一个字符串名称数组,用于在 jQuery 处理原生浏览器事件时始终复制的属性。(通过 .trigger()
在代码中触发的事件不使用此列表,因为代码可以使用所需值构造一个 jQuery.Event
对象并使用该对象触发。)
要向此列表添加属性名称,请使用 jQuery.event.props.push( "newPropertyName" )
。但是,请注意,jQuery 处理的每个事件现在都将尝试将此属性名称从原生浏览器事件复制到 jQuery 构建的事件。如果该事件类型不存在该属性,它将获得一个未定义的值。向此列表添加许多属性会显著降低事件传递性能,因此对于不经常需要的属性,直接从 event.originalEvent
使用值会更有效。如果必须复制属性,强烈建议您从 1.7 版开始使用 jQuery.event.fixHooks
。
链接 jQuery.event.fixHooks:对象
fixHooks
接口提供了一种按事件类型的方式来扩展或规范化 jQuery 在处理原生浏览器事件时创建的事件对象。fixHooks
条目是一个有两个属性的对象,每个属性都是可选的
props
:数组:表示应从浏览器的事件对象复制到 jQuery 事件对象的属性的字符串。如果省略,则不会复制超出 jQuery 复制和规范化的标准属性之外的任何其他属性(例如 event.target
和 event.relatedTarget
)。
filter
:函数 (event, originalEvent):jQuery 在构造 jQuery.Event
对象、从 jQuery.event.props
复制标准属性并复制上述 fixHooks
特定的 props(如果有)后调用此函数。该函数可以在事件对象上创建新属性或修改现有属性。第二个参数是浏览器的原生事件对象,它在 event.originalEvent
中也可用。
请注意,对于所有事件,浏览器的原生事件对象在 event.originalEvent
中可用;如果 jQuery 事件处理程序检查那里的属性而不是 jQuery 的规范化 event
对象,则无需创建 fixHooks
条目来复制或修改属性。
例如,要为“drop”事件设置一个挂钩来复制dataTransfer
属性,请将一个对象分配给jQuery.event.fixHooks.drop
1
2
3
|
|
由于fixHooks
是一个高级特性,并且很少在外部使用,jQuery不包含处理冲突解决的代码或接口。如果存在其他一些代码可能将fixHooks
分配给相同事件的可能性,则代码应检查现有挂钩并采取适当措施。一个简单的解决方案可能如下所示
1
2
3
4
5
6
7
|
|
当存在已知情况下不同的插件想要附加到drop挂钩时,此解决方案可能更合适
1
2
3
4
5
6
7
8
9
10
11
12
13
|
|
link 特殊事件挂钩
jQuery特殊事件挂钩是一组针对每个事件名称的函数和属性,允许代码控制jQuery中事件处理的行为。该机制类似于fixHooks
,因为特殊事件信息存储在jQuery.event.special.NAME
中,其中NAME
是特殊事件的名称。事件名称区分大小写。
与fixHooks
一样,特殊事件挂钩设计假定两个不相关的代码块想要处理同一个事件名称的情况非常罕见。需要使用现有挂钩修改事件的特殊事件作者需要采取预防措施,以避免通过破坏这些挂钩而引入不必要的副作用。
link noBubble: 布尔值
指示当调用.trigger()
方法时是否应该冒泡此事件类型;默认情况下为false
,这意味着触发事件将冒泡到元素的父级,直到文档(如果附加到文档)然后到窗口。请注意,在事件上定义noBubble
将有效阻止该事件用于带有.trigger()
的委托事件。
link bindType: 字符串,delegateType: 字符串
在定义时,这些字符串属性指定特殊事件应像其他事件类型一样处理,直到事件被传递。如果事件直接附加,则使用 bindType
,而 delegateType
用于委托事件。这些类型通常是 DOM 事件类型,本身不应是特殊事件。
通过一个示例,可以最容易地了解这些属性的行为。假设定义了一个特殊事件,如下所示
1
2
3
4
|
|
在定义这些属性时,jQuery 事件系统中会发生以下行为
- “pushy”事件的事件处理程序实际上附加到“click”——直接绑定和委托事件。
- 如果存在“click”特殊事件钩子,则会调用它们,但如果存在,则在传递事件时会调用“pushy”的
handle
钩子。 - 必须使用“pushy”事件名称删除“pushy”事件处理程序,并且如果从同一元素中删除“click”事件,则不会受到影响。
因此,给定上述特殊事件,此代码显示通过删除点击不会删除 pushy。例如,这可能是一种抵御未命名空间其删除点击事件的错误插件的有效方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
|
这两个属性通常与 handle
钩子函数结合使用;例如,该钩子可以在调用事件处理程序之前将事件名称从“click”更改为“pushy”。请参阅以下示例。
link handleObj 对象
下面许多特殊事件钩子函数传递了一个 handleObj
对象,该对象提供了有关事件、其附加方式及其当前状态的更多信息。此对象及其内容应视为只读数据,并且仅以下属性记录供特殊事件处理程序使用。对于以下讨论,假设使用此代码附加了一个事件
1
2
3
|
|
type
:字符串:事件类型,例如 "click"
。当通过 bindType
或 delegateType
使用特殊事件映射时,这将是映射类型。
origType
:字符串:原始类型名称(在本例中为 "click"
),无论它是否通过 bindType
或 delegateType
映射。因此,当“pushy”事件映射到“click”时,其 origType
将为“pushy”。有关更多详细信息,请参阅上面那些特殊事件属性中的示例。
namespace
:字符串:在附加事件时提供的命名空间(如果有的),例如 "myPlugin"
。当提供多个命名空间时,它们将用句点分隔并按升序字母顺序排序。如果没有提供命名空间,则此属性为空字符串。
selector
:字符串:对于委托事件,这是用于筛选后代元素并确定是否应调用处理程序的选择器。在示例中,它是 "button"
。对于直接绑定的事件,此属性为 null
。
data
:对象:在事件绑定期间传递给 jQuery 的数据(如果存在),例如 { myData: 42 }
。如果省略了 data 参数或为 undefined
,则此属性也为 undefined
。
handler
:function( event: jQuery.Event ):在事件绑定期间传递给 jQuery 的事件处理程序函数;在示例中,它引用 myHandler
。如果在事件绑定期间传递了 false
,则处理程序引用一个仅返回 false
的共享函数。
link setup: function( data: Object, namespaces, eventHandle: function )
首次将特定类型的事件附加到元素时,将调用 setup 钩子;这为钩子提供了执行处理的机会,该处理将应用于此元素上的此类型的全部事件。this
关键字将引用附加事件的元素,eventHandle
是 jQuery 的事件处理程序函数。在大多数情况下,不应使用 namespaces
参数,因为它仅表示正在附加的第一个事件的命名空间;后续事件可能没有此命名空间。
此钩子可以执行它希望的任何处理,包括将自己的事件处理程序附加到元素或其他元素,并使用 jQuery.data()
方法记录元素上的设置信息。如果 setup 钩子希望 jQuery 添加浏览器事件(取决于浏览器,通过 addEventListener
或 attachEvent
),它应返回 false
。在所有其他情况下,jQuery 不会添加浏览器事件,但会继续为事件执行所有其他簿记。例如,如果事件从未由浏览器触发但由 .trigger()
调用,则这将是合适的。若要在 setup 钩子中附加 jQuery 事件处理程序,请使用 eventHandle
参数。
link teardown: function()
当从元素中移除特定类型的最后一个事件时,将调用 teardown 钩子。this
关键字将引用正在清理事件的元素。如果此钩子希望 jQuery 从浏览器的事件系统中移除事件(通过 removeEventListener
或 detachEvent
),则应返回 false
。在大多数情况下,setup 和 teardown 钩子应返回相同的值。
如果 setup 钩子通过 jQuery.data()
等机制附加了事件处理程序或向元素添加了数据,则 teardown 钩子应逆转该过程并将其移除。当元素从文档中完全移除时,jQuery 通常会移除数据和事件,但如果在 teardown 时未能移除数据或事件,则如果元素保留在文档中,将导致内存泄漏。
link add: function( handleObj )
每次通过 .on()
等 API 向元素添加事件处理程序时,jQuery 都会调用此钩子。this
关键字将是向其添加事件处理程序的元素,handleObj
参数如上文所述。此钩子的返回值将被忽略。
link remove: function( handleObj )
当使用诸如 .off()
的 API 从元素中移除事件处理程序时,将调用此挂钩。this
关键字将是处理程序被移除的元素,并且 handleObj
参数如上文所述。此挂钩的返回值将被忽略。
link trigger: function( event: jQuery.Event, data: Object )
当 .trigger()
或 .triggerHandler()
方法用于从代码触发特殊类型的事件(而不是源自浏览器内部的事件)时调用。this
关键字将是被触发的元素,并且事件参数将是从调用者的输入构建的 jQuery.Event
对象。至少,事件类型、数据、命名空间和目标属性将设置在事件上。如果存在,data 参数表示由 .trigger()
传递的其他数据。
在触发事件的过程中,在 jQuery.Event
对象构建之后且在任何处理程序被调用之前,将调用触发挂钩。它可以以任何方式处理触发的事件,例如在返回之前调用 event.stopPropagation()
或 event.preventDefault()
。如果挂钩返回 false
,jQuery 不会执行任何进一步的事件触发操作,并立即返回。否则,它将执行正常的触发处理,为元素调用任何事件处理程序,并将事件冒泡(除非提前停止传播或为特殊事件指定了 noBubble
)以调用附加到父元素的事件处理程序。
link _default: function( event: jQuery.Event, data: Object )
当 .trigger()
方法完成为事件运行所有事件处理程序时,它还会查找并运行目标对象上具有相同名称的任何方法,除非处理程序调用 event.preventDefault()
。因此,.trigger( "submit" )
将在元素上执行 submit()
方法(如果存在)。当指定 _default
挂钩时,该挂钩在检查和执行元素的默认方法之前调用。如果此挂钩返回 false
值,则将调用元素的默认方法;否则不调用。
link handle: function( event: jQuery.Event, data: Object )
当事件发生且 jQuery 通常会调用由 .on()
或其他事件绑定方法指定的用户的事件处理程序时,jQuery 会调用处理挂钩。如果挂钩存在,jQuery 会调用它(而不是该事件处理程序),向它传递事件和从 .trigger()
传递的任何数据(如果它不是原生事件)。this
关键字是被处理的 DOM 元素,并且 event.handleObj
属性具有详细的事件信息。
基于其具有的信息,句柄钩子应决定是否调用原始处理程序函数,该函数位于event.handleObj.handler
中。它可以在调用原始处理程序之前修改事件对象中的信息,但必须在返回之前恢复该数据,否则后续不相关的事件处理程序可能会表现得不可预测。在大多数情况下,句柄钩子应返回原始处理程序的结果,但这由钩子自行决定。句柄钩子是唯一的,因为它是唯一在使用bindType
和delegateType
映射类型时在其原始特殊事件名称下被调用的特殊事件函数钩子。因此,如果特殊事件定义了bindType
和delegateType
,则几乎总是错误地存在除句柄钩子之外的任何其他内容,因为这些其他钩子将永远不会被调用。
链接 示例:多重点击事件
此multiclick
特殊事件将自身映射到标准点击事件中,但使用句柄钩子以便它可以监视事件,并且仅在用户在事件绑定期间指定的次数的倍数点击元素时才传递该事件。
钩子将当前点击计数存储在数据对象中,因此不同元素上的多重点击处理程序不会相互干扰。它在调用处理程序之前将事件类型更改为原始“multiclick”类型,并在返回之前将其恢复为映射的“click”类型
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
|
|