Javascript 自定义事件
作为异步驱动编程模型,事件无疑是JavaScript不可或许的部分。
在JavaScript所定义的事件中,它们被分为多种类型。有 表单事件(focus、blur、submit等)、window事件(load、unload、resize等)、鼠标事件(mousedown、mousmove、mousup等),键盘类型(keydown、keyup等)。
针对移动端,还有触摸类型,比如 touchstart、touchmove、touchend、touchcancel 等。另外,HTML5还有各类API事件,比如 dragstart、drag、dragend 等。
当然,上面所涉及到类型,都是系统提供的。要响应点击事件,我们通常会这样:
element.addEventListener('click', clickFn); |
当用户点击 element
时,就会触发函数 clickFn
。但你有没有想过,为什么当我们点击元素后,就会触发绑定的函数呢?
换言之,如果把 click
事件换成我们自定义的事件,比如系统没有提供的长按事件,回调函数也会执行吗?
这里假设用户点击元素,按住时间超过1秒,即为 长按
,我们定义事件名为 longpress
。当用户执行下面代码,longpressFn
能否也能触发?
element.addEventListener('longpress', longpressFn); |
若你运行过上面的代码,你会发现 longpressFn
并不会执行。原因是,longpress
并非是JavaScript定义的事件,而是我们自定义的。因此无法识别,longpressFn
自然也不会触发。
这里的 longpress
就是所谓的 “自定义事件”,也被称为 “模拟事件”。
现在,我们面临的问题是如何让自定义的 longpress
事件生效。而要解决这个问题,通常需要经历 创建自定义事件 和 触发自定义事件 这两步。
创建自定义事件
JavaScript提供了自定义事件的API,即 CustomEvent()
,它的使用语法如下:
var evt = new CustomEvent(typeArg, customEventInit); |
其中,typeArg
是一个字符串,表示要创建的事件类型。而 customEventInit
是一个可选对象,它包含以下几个属性:
detail
任意的数据类型,表示事件初始化时,需要传递的数据,默认值为null
bubbles
布尔值,表示该事件能否冒泡,默认值为false
cancelable
布尔值,表示该事件是否可以取消,默认值为false
所以,我们可用使用以下代码去创建一个自定义事件:
var evt = new CustomEvent(type, {detail: msg, bubbles: true, cancelable: true}); |
旧版写法
而针对旧版浏览器,创建事件还有一个已废弃的写法(有的旧浏览器支持)。你必须先在 document
对象上使用 createEvent
来创建一个新的 event对象,如:
var evt = document.createEvent(type); |
其中,evt
是被创建的 Event对象
。type
是一个字符串,表示要创建的事件类型。它包含 “DOM2级事件” 和 “DOM3级事件”,还有其他的规范定义(如SVG),另外,每个事件类型都有其对应的初始化方法。下面列出部分:
DOM2级
事件模块 | 事件类型 | 事件初始化方法 |
---|---|---|
UI事件 | “UIEvents” | event.initUIEvent |
鼠标事件 | “MouseEvents” | event.initMouseEvent |
DOM变动事件 | “MutationEvents” | event.initMutationEvent |
HTML事件 | “HTMLEvents” | event.initEvent |
DOM3级
事件模块 | 事件类型 | 事件初始化方法 |
---|---|---|
… | … | … |
自定义事件 | “CustomEvent” | event.initCustomEvent |
… | … | … |
更多事件类型,可参见 相关文档。
这里,我们要自定义事件,选择的事件类型为 CustomEvent
,所以需要这样创建:
var evt = document.createEvent('CustomEvent'); |
对应的事件初始化,就是这样的:
evt.initCustomEvent(type, canBubble, cancelable, detail); |
其中,type
为字符串,表示事件类型。bubbles
为布尔值,决定是否事件是否应该向上冒泡。cancelable
决定该事件的默认动作是否可以被取消,即 是否可以用 preventDefault() 方法取消事件。detail
表示初始化事件时,需要传递的数据。
触发自定义事件
定义完事件后,接下来便是触发该事件了。也就是在什么时机下,触发下面的 longpressFn
函数:
element.addEventListener('longpress', longpressFn); |
与繁杂的 创建自定义事件 事件相比,触发回调函数十分简单,只需要调用 dispatchEvent
函数,像这样:
element.dispatchEvent(evt); |
此时,自定义事件绑定的回调函数便会执行。
结合上面的 创建自定义事件,我们进行简单封装:
function triggerCustomEvent(element, type, msg) { |
实现一个长按事件
通过前面文章的描述,我们可以轻松的实现一个自定义的长按事件:
var longpressTimer, mousedownTime; |
上面的代码中,记录分别读取了鼠标按下和鼠标释放事件的时间,若这两个事件的时间间隔超过一秒时,便触发自定义事件的回调函数。
当你在 element
元素上长按时间超过一秒时,控制台会打印出 你长按了!
。
这样,一个自定义的长按事件就完成了。
IE中的自定义事件
虽说前面的自定义事件看似已经比较完美了,但很可惜,针对前面提到的 创建自定义事件 和 触发自定义事件, IE并不支持。
有人可能知道,要在 IE 浏览器中完成事件模拟,可以把 document.createEventObject
和 fireEvent
结合起来。像这样:
var evt; |
但很快你就会发现,这里的 fireEvent
就是一坑货。因为它只支持IE已经定义了的事件,并不能实现真正意义上的实现自定义事件。
如果非要在IE中实现自定义事件,也是有办法的。其中,网上比较流行的一种做法,是利用IE的私有事件 propertychange
。
原理:如果元素绑定了 propertychange
事件,那么,只要该元素的属性发生变化,propertychange
所对应的回调函数就能触发。这样,我们便可以将自定义事件(longpress
)所对应的回调函数(longpressFn
),放在 propertychange
的回调函数中调用。
用代码简单描述下:
... |
如果希望尽可能多的兼容,可将标准DOM和IE的自定义事件再进行组合封装。
以上,就是JavaScript自定义事件。