Event对象与自定义事件

事件对象

当为DOM元素添加一些事件的回调函数后,事件被触发时,会传给回调函数一个Event对象作为第一个参数;而这个Event对象包含关于触发的事件的一些信息及处理方法。

常用属性

  1. bubblesBoolean(只读), 表明事件是否冒泡;
  2. cancelableBoolean (只读), 表明是否可以取消事件的默认行为;
  3. currentTargetElement (只读),事件处理程序当前正在处理事件的那个元素;
  4. defaultPreventedBoolean (只读), 为true表示已经调用了preventDefault()DOM3级事件中新增)
  5. detailInteger (只读),与事件相关的细节信息;
  6. eventPhaseInteger (只读),调用事件处理程序的阶段: 1表示捕获阶段,2表示『处于目标』(即currentTargettarget一致时),3表示冒泡阶段;
  7. targetElement (只读),事件的目标;
  8. typeString (只读),被触发的事件的类型;
  9. viewAbstractView(只读),与事件关联的抽象视图。等同于发生事件的window对象;

整理自《JavaScript 高级程序设计》

常用方法

  1. preventDefault()Function(只读),取消事件的默认行为。如果cancelabletrue,则可以使用这个方法;
  2. stopImmediatePropagation()Function(只读),取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用DOM3级事件中新增)
  3. stopPropagation()Function(只读),取消事件的进一步捕获或冒泡。如果bubblestrue,则可以使用这个方法;

整理自《JavaScript 高级程序设计》

DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

构建自定义事件

构建一个自定义事件的关键就是:

  1. 拦截原生事件,阻断原事件传播:利用addEventlistener的第三个参数,可以在某父级DOM上事件传播的捕获阶段触发回调函数,然后使用stopPropagation方法阻止原事件的进一步传递;

  2. 构造自定义Event对象:利用Event构造函数构造一个自定义属性的事件对象;

  3. 派发自定义事件:使用EventTarget对象自带的dispatchEvent方法进行事件对象的派发;

addEventlistener方法

1
target.addEventListener(type, listener[, useCapture])

addEventListener可以为DOM绑定一个DOM2级事件的监听处理,一般第三个参数useCapture很少使用,其作用就是该事件是否在捕获阶段触发,默认为false,因此设置为true时可在捕获阶段触发监听处理函数!

创建Event对象

event = new Event(typeArg, eventInit);

通过Event构造函数可以创建一个自定义属性的Event对象,相当于回调函数接收的第一个参数;

  • typeArg:事件的名称;
  • eventInit:事件的设置选项,为一个对象,该对象可以使用以下属性:
    • bubblesBoolean类型,表示该事件是否可以冒泡,默认值为false
    • cancelableBoolean类型,表示该事件是否可以使用preventDefault()方法进行取消,默认为false
    • composedBoolean类型,事件是否会在影子DOM根节点(shadow root DOM)之外触发侦听器,默认为false

dispatchEvent()

EventTarget.dispatchEvent(event)

  • EventTarget:一般为触发事件时Event对象的target属性所指向的DOM节点;
  • eventEvent对象;

使用该方法就相当于某个DOM节点触发了某个event事件;

案例:自定义tap事件

由于现在的移动端设备的屏幕多是触摸屏,因此H5针对触摸屏特别制定了适用于触摸设备的TouchEvent事件规范,这使得触摸屏的点击事件变得更加灵敏了;

为啥使用click事件就不灵敏了?由于一些历史原因,现在的移动端浏览器对于click事件和touch event事件的执行顺序遵守如下规则:touchstart->touchmove->touchend->wait 300ms->click;没错,click要在所有的touch event执行完后延迟300ms再执行;

因此,如果在移动端使用click事件在点击时会出现一个很明显的延迟,对于用户体验来说是不利;其中一个解决办法就是利用TouchEvent封装一个tap点触事件,消除300ms的延迟:

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
function initTap(eventType = 'tap') {
let isTap = false

document.body.addEventListener('touchstart', e => {
isTap = true
}, true)

document.body.addEventListener('touchmove', e => {
isTap = false
}, true)

document.body.addEventListener('touchend', e => {
if(isTap) {
e.stopPropagation() // 阻止原事件继续传递
let tapEvent = new Event(eventType, {
bubbles: true, // 可以冒泡
cancelable: false // 没有默认行为,不可取消默认行为
}) // 构建自定义tap事件(点触)
tapEvent.touches = e.touches // 保留touchEvent的触点信息
tapEvent.targetTouches = e.targetTouches
tapEvent.changedTouches = e.changedTouches
e.target.dispatchEvent(tapEvent) // 向target派发事件
}
}, true)
}

上面给出的initTap方法实际上是在body元素上(也就是全局)劫持TouchEvent事件,只要判断不是touchmove事件,那么在touchend事件阶段就会在target对象上派发一个自定义的tap事件,即相当于在touchend事件后马上触发,因此也就没有click事件的300ms延迟,反应特别灵敏;这种方法可以直接在Vue中利用v-on/@进行监听(如@tap="xxx"),十分方便!

而且TouchEvent在移动端的兼容性非常好,可以大胆的使用:

mark

参考文档

  1. Event() | MDN
  2. 《JavaScript 高级程序设计》
  3. EventTarget.dispatchEvent() | MDN
  4. TouchEvent | MDN
  5. EventTarget.addEventListener() | MDN
  6. 移动端Click300毫秒点击延迟的来龙去脉(转) - 低调的大白兔 - 博客园
  7. https://github.com/ustbhuangyi/better-scroll/blob/2e9513bd6d0bb1a1fd2a17af172ef600c70dadfd/src/util/dom.js#L137