今天有仔細看了一遍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()很常見,我們在修改內核或應用時都能經常用到。