浏览器事件链原理

浏览器事件链

绑定事件

1
2
3
4
5
//options: 
// capture : Boolean
// once : Boolean
// passive : Boolean
element.addEventListener(type, listener, options | useCapture)
1
2
3
4
//内部结构:
EventListenerMap
key : type
value: [{listener, options}, ...]

触发事件

从target_element上开始触发事件

事件类型

  • Event:CAPTURING_PHASE (捕获事件,从父元素到子元素)
  • Event:AT_TARGET (命中事件,触发元素)
  • Event:BUBBLING_PHASE (冒泡事件,从子元素到父元素)

事件链EventPath

EventPath = [targetElement, …, HTMLBodyElement, HTMLHtmlElement, HTMLDocument, HTMLDocument]
第2个HTMLDocument的currentTarget为DomWindow

事件执行过程

  • CAPTURING_PHASE:path=[EventPath.last,…,EventPath.first+1] ==>currentTarget->fireEventListeners
  • AT_TARGET:path=[EventPath.first] ==>currentTarget->fireEventListeners
  • BUBBLING_PHASE:path=[EventPath.first+1,…,EventPath.last] ==> currentTarget->fireEventListeners

问题

当一个Element上同时绑定capture和bubbling事件时,如何触发?

  • 非At_TARGET的Element,按照正常执行顺序,先执行capture路径,再target,再bubbling路径
  • 在At_TARGET 的Element, 只按照绑定时候的顺序执行,不区分capture和bubbling

原因:

1
2
3
4
5
6
7
8
//fireEventListeners时
//CAPTURING_PHASE和BUBBLING_PHASE过程时,如果eventPhase和useCapture不匹配,
//会被拦截后 continue,
//但是在At_TARGET的过程没有这层拦截
if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener->useCapture())
continue;
if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener->useCapture())
continue;