今天有仔细看了一遍webkit中event的dispatch机制,整理如下(WebKit-r60688,最新版本中总体流程没变)
bool Node::dispatchGenericEvent(PassRefPtr<Event> prpEvent)
{
......
Vector<RefPtr<ContainerNode> > ancestors;
eventAncestors(ancestors); // 1. 获取该Node的所有祖先节点
DOMWindow* targetForWindowEvents = 0;
if (event->type() != eventNames().loadEvent) { // 2. 非loadEvent,获取DOMWindow对象,即对应JS的window对象
Node* topLevelContainer = ancestors.isEmpty() ? this : ancestors.last().get();
if (topLevelContainer->isDocumentNode())
targetForWindowEvents = static_cast<Document*>(topLevelContainer)->domWindow();
}
void* data = preDispatchEventHandler(event.get()); // 3. event预处理preDispatch
if (event->propagationStopped()) goto doneDispatching;
event->setEventPhase(Event::CAPTURING_PHASE); // 4. event的caputre阶段处理。从DOMWindow,到Document, ... ,直到target node
if (targetForWindowEvents) {
event->setCurrentTarget(targetForWindowEvents);
targetForWindowEvents->fireEventListeners(event.get());
if (event->propagationStopped())
goto doneDispatching;
}
for (size_t i = ancestors.size(); i; --i) {
ContainerNode* ancestor = ancestors[i - 1].get();
event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor));
ancestor->handleLocalEvents(event.get());
if (event->propagationStopped())
goto doneDispatching;
}
event->setEventPhase(Event::AT_TARGET); // 5. event的target阶段处理
event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this));
handleLocalEvents(event.get());
if (event->propagationStopped()) goto doneDispatching;
if (event->bubbles() && !event->cancelBubble()) {
// Trigger bubbling event handlers, starting at the bottom and working our way up.
event->setEventPhase(Event::BUBBLING_PHASE); // 6. 如果event允许bubble,且cancelbubble为false(即不允许设置取消bubble),
//进入event的bubble阶段处理方向与capture相反,从target node ... Document,DOMWindow
size_t size = ancestors.size();
for (size_t i = 0; i < size; ++i) {
ContainerNode* ancestor = ancestors[i].get();
event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor));
ancestor->handleLocalEvents(event.get());
if (event->propagationStopped() || event->cancelBubble())
goto doneDispatching;
}
if (targetForWindowEvents) {
event->setCurrentTarget(targetForWindowEvents);
targetForWindowEvents->fireEventListeners(event.get());
if (event->propagationStopped() || event->cancelBubble())
goto doneDispatching;
}
}
doneDispatching:
postDispatchEventHandler(event.get(), data); // 7. 三阶段处理结束后,event后处理postDispatch
if (!event->defaultPrevented() && !event->defaultHandled()) {
defaultEventHandler(event.get()); // 8. 如果3阶段处理完毕,event的defaultPrevented仍然为false且defaultHandled也为false,即事件的默认处理不被阻止且还没有
// 被处理,则调用target node的默认event处理函数defaultEventHandler
// event的defaultPrevented表示js的listener阻止event的进一步处理。event的defaultHandled表示3阶段处理过程中event
// 是否被处理了
if (event->defaultHandled())
goto doneWithDefault;
if (event->bubbles()) { // 9. 如果此时event还没有被处理掉(即node的defaultEventHandler也没有处理该事件)且event能bubble(可以查看eventhandler,
// 哪些event创建时bubble时true,默认Event的bubble是false,UIEvent的bubble为true),
// 则以bubble方式调用defaultEventHandler
size_t size = ancestors.size();
for (size_t i = 0; i < size; ++i) {
ContainerNode* ancestor = ancestors[i].get();
ancestor->defaultEventHandler(event.get());
ASSERT(!event->defaultPrevented());
if (event->defaultHandled())
goto doneWithDefault;
}
}
}
doneWithDefault:
return !event->defaultPrevented(); // 10. 最后返回event的defaultPrevented,决定该event是否被处理掉了
}
总结一下:
处理event的event target有,从顶到低: DOMWindow,Document, ... , targetNode
首先capture, target, bubble(event允许bubble时) 三阶段处理(主要是user通过js addEventListerner注册的函数)
如果事件还没有被处理,则调用target node的defaultEentHandler处理(WebKit内核中事件默认处理函数)
如果事件还没有被处理,则以bubble方式调用defaultEentHandler(前提是event允许bubble)
capture, target, bubble三阶段事件处理函数是handleLocalEvents,该函数主要在Node.cpp中实现(chromium39中在HTMLFormElement中重载实现了,其他element没有),主要作用就是触发fireEventListeners,即交给js处理
JS调用addEventListener的第三个参数capture,意义是“是否capture”,即是否在capture过程处理,如果为true,则在capture阶段就处理,否则在bubble阶段处理。如果javascript某一个处理函数将event的cancelBubble属性设置为true,则target之后的bubble处理过程就会被取消,即通过addEventListener且设置了capture为false的js函数就不在被执行。上述代码中的event->cancelBubble()即为event的cancelBubble的属性值。
event->bubbles()表示该event是否能够bubble,在该event创建时已经确定了。所以说并不是所有的event都能bubble处理的。UIEvent一般是不能bubble的,也有些特殊的(chromium39中搜索UIEvent::create查看,例如EventTypeNames::DOMActivate是可以bubble的)
event->cancelable()表示该event是否能被取消。
event->defaultPrevented()表示该event是否被阻止了
event->defaultHandled()表示该event已经被处理过了且不需要继续传递。event.setDefaultHandled()很常见,我们在修改内核或应用时都能经常用到。