当事件发生在某个文档节点上时(即事件目标),目标的事件处理程序就会被触发。此外目标的每个祖先节点也有机会处理该事件。
2级DOM的事件传播包含三个阶段:
- 捕捉阶段(capturing),事件从顶级文档树节点一级一级向下遍历,直到到达该事件的目标节点。
- 到达事件的目标节点,执行目标节点的时间处理程序。
- 事件起泡(bubbling),事件从目标节点一级一级向上上溯,直到顶级文档树节点。
相应的,2级DOM通过下面的两个函数给节点对象添加和删除事件处理函数。
addEventListener(eventType, handler, propagate);
removeEventListener(eventType, handler, propagate);
三个参数意思分别如下:
- eventType: 即事件类型(不加on)。比如:"click"。
- handler: 事件处理函数。传入参数即为事件对象event。
- propagate: 是否只执行捕获和目标节点两个阶段。true的话,只执行1,2两个阶段;false的话,只指向2,3两个阶段。
IE的事件传播只包含上边的2和3两个阶段
相应的,IE通过下面两个函数给节点对象添加和删除事件处理函数。
attachEvent(eventType, handler);
detachEvent(eventType, handler);
参数意思同2级DOM对应的函数参数。
下面是程序的部分执行结果:
1. 开启捕捉过程,点击SetEventHandler按钮之后,点击Click Me按钮,观察捕捉过程中的事件响应


2. 开启阻止起泡过程,点击SetEventHandler按钮之后,点击Click Me按钮,观察起泡过程中的事件响应


以下是Sample的代码,分别给window, document, document.body, divOut, divIn和btnClick针对不同的浏览器追加了事件处理函数,并且在画面上列出执行时候的可配置项,动态模拟各种配置项对事件传播过程的影响,同时也将各种情况下的执行结果显示到了页面上。浏览器(Chrome, IE)。
<html>
<head>
<script type="text/javascript">
var disablePropagation = true;
var cancelBubble = false;
//Simple judge of browser
var isIE = window.attachEvent ? true : false;
//Short name for document.getElementById
var byId = function(id) {
return document.getElementById(id);
}
// Has event handler already been set.
var isEventHandlerSetted = false;
// value setted for Enable Propagate Checkbox at last time.
var preisEnablePropagate = false;
// event happend at last time.
var preEvent = null;
// Set Event Handler for elements
function setEventHandler() {
clearTrace();
// remove setted event handlers at last time
if (isEventHandlerSetted) {
if (isIE) {
window.detachEvent('onclick', prtDtlWindow);
document.detachEvent('onclick', prtDtlDocument);
document.body.detachEvent('onclick', prtDtlBody);
byId('divOut').detachEvent('onclick', prtDtlDivOut);
byId('divIn').detachEvent('onclick', prtDtlDivIn);
byId('btnClick').detachEvent('onclick', prtDtlClickMeBtn);
} else {
window.removeEventListener('click', prtDtl, preisEnablePropagate);
document.removeEventListener('click', prtDtl, preisEnablePropagate);
document.body.removeEventListener('click', prtDtl, preisEnablePropagate);
byId('divOut').removeEventListener('click', prtDtl, preisEnablePropagate);
byId('divIn').removeEventListener('click', prtDtl, preisEnablePropagate);
byId('btnClick').removeEventListener('click', prtDtl, preisEnablePropagate);
}
}
// Add new event handlers according to new setting.
var isEnablePropagate = byId("cbxEnablePropagate").checked;
preisEnablePropagate = isEnablePropagate;
if (isIE) {
window.attachEvent('onclick', prtDtlWindow);
document.attachEvent('onclick', prtDtlDocument);
document.body.attachEvent('onclick', prtDtlBody);
byId('divOut').attachEvent('onclick', prtDtlDivOut);
byId('divIn').attachEvent('onclick', prtDtlDivIn);
byId('btnClick').attachEvent('onclick', prtDtlClickMeBtn);
} else {
window.addEventListener('click', prtDtl, isEnablePropagate);
document.addEventListener('click', prtDtl, isEnablePropagate);
document.body.addEventListener('click', prtDtl, isEnablePropagate);
byId('divOut').addEventListener('click', prtDtl, isEnablePropagate);
byId('divIn').addEventListener('click', prtDtl, isEnablePropagate);
byId('btnClick').addEventListener('click', prtDtl, isEnablePropagate);
}
isEventHandlerSetted = true;
}
// a series of event handler for IE
function prtDtlWindow() {
prtDtl(window.event, window);
}
function prtDtlDocument() {
prtDtl(window.event, document);
}
function prtDtlBody() {
prtDtl(window.event, document.body);
}
function prtDtlDivOut() {
prtDtl(window.event, byId ('divOut'));
}
function prtDtlDivIn() {
prtDtl(window.event, byId ('divIn'));
}
function prtDtlClickMeBtn() {
prtDtl(window.event, byId ('btnClick'));
}
// print detail formatted event handler execution info.
function prtDtl(e, currentTarget) {
if (!isSameEvent(preEvent, e)) {
clearTrace();
}
var target = null;
var curTarget = null;
if (isIE) {
target = e.srcElement;
curTarget = currentTarget
} else {
target = e.target;
curTarget = e.currentTarget;
}
if (!isSameEvent(preEvent, e))
trace(target, true);
trace(curTarget, false);
if (isIE) {
/* when I hold e directively ussing preEvent, preEvent's srcElement.id
is the same as e's srcElement.id even when I click again.
So I use preEvent to hold e's srcElement.id directively.
(It seems the preEvent and e always share the same reference, but this
is conflict with the result [preEvent === e] which returns false.)
Maybe IE uses a completely different logic when compare Event Object I guess,
who hnows.*/
//preEvent = e;
preEvent = target.id;
} else {
preEvent = e;
}
var isCclBubble = byId("cbxCclBubble").checked;
var isEnablePropagate = byId("cbxEnablePropagate").checked;
if (isCclBubble || isEnablePropagate) {
stopEvent(e, curTarget);
}
}
function trace(t, isTarget) {
var targetName = "";
if (t === window) {
targetName = "Window";
} else if (t === document) {
targetName = "Document";
} else if (t.getAttribute != null){
targetName = t.getAttribute('detail');
} else {
for (var p in t) {
if (p.indexof('name') != -1) {
targetName = p;
break;
}
}
}
var traceTxt = byId('traceArea');
if (isTarget) {
var browser = "Not IE";
if (isIE)
browser = "IE";
traceTxt.value += ("Your Browser is " + browser + ". \n");
traceTxt.value += ("Event Target is [" + targetName + "]. \n\n\n");
} else {
traceTxt.value += ("[" + targetName + "]'s handling function called. \n");
}
}
function stopEvent(e, curTarget) {
var stopAtWindow = byId("cbxWindow").checked;
var stopAtDocument = byId("cbxDocument").checked;
var stopAtBody = byId("cbxBody").checked;
var stopAtOutDiv = byId("cbxOutDiv").checked;
var stopAtInDiv = byId("cbxInDiv").checked;
var stopAtClickMeBtn = byId("cbxClickMeBtn").checked;
switch (curTarget) {
case window:
stopEventIn(e, stopAtWindow);
break;
case document:
stopEventIn(e, stopAtDocument);
break;
case document.body:
stopEventIn(e, stopAtBody);
break;
case byId("divOut"):
stopEventIn(e, stopAtOutDiv);
break;
case byId("divIn"):
stopEventIn(e, stopAtInDiv);
break;
case byId("btnClick"):
stopEventIn(e, stopAtClickMeBtn);
break;
default:
;
}
}
function stopEventIn(e, stop) {
if (!stop)
return;
if (isIE) {
e.cancelBubble = true;
return;
}
e.stopPropagation();
}
function clearTrace() {
byId("traceArea").value = "";
}
/* It is strange that IE create several event object for
different elements' event handler.
although user only click once. why????? TODO */
function isSameEvent(preEvent, e) {
if (preEvent == null) {
return false;
}
if (isIE) {
/* It seems preEvent and e share same reference because that even
when i click again and again,
preEvent.srcElement.id always has the same value with e.srcElement.id */
// return (preEvent.srcElement.id == e.srcElement.id);
return (preEvent == e.srcElement.id);
}
return preEvent===e;
}
</script>
</head>
<body detail="Body">
<table><tr>
<td>
<div id="divOut" detail="outter div" style="height:300; width:300; border:1 red solid">
<div id="divIn" detail="inner div" style="height:200; width:200; border:1 blue solid">
<button id="btnClick" type="button" detail="click me button">Click Me</button>
</div>
</div>
</td>
<td>
<div id="divSet" detail="Setting div" style="height:300; width:300; border:1 red solid">
<table>
<tr><td><button type="button" detail="Setting button" onclick="setEventHandler()">Set EventHandler</button></td></tr>
<tr><td><label>
<input id="cbxEnablePropagate" detail="Enable Propagate Checkbox" type="checkbox"/>Enable Propagate ?</label></td></tr>
<tr><td><label><input id="cbxCclBubble" detail="Cancel Bubble Checkbox" type="checkbox"/>Cancel Bubble ?</label></td></tr>
<tr><td><label><input id="cbxWindow" detail="Stop At Window Checkbox" type="checkbox"/>Stop At Window ?</label></td></tr>
<tr><td><label><input id="cbxDocument" detail="Stop At Docuement Checkbox" type="checkbox"/>Stop At Docuement ?</label></td></tr>
<tr><td><label><input id="cbxBody" detail="Stop At Body Checkbox" type="checkbox"/>Stop At Body ?</label></td></tr>
<tr><td><label><input id="cbxOutDiv" detail="Stop At Outdiv Checkbox" type="checkbox"/>Stop At OutDiv ?</label></td></tr>
<tr><td><label><input id="cbxInDiv" detail="Stop At InDiv Checkbox" type="checkbox"/>Stop At InDiv ?</label></td></tr>
<tr><td><label><input id="cbxClickMeBtn" detail="Stop At Click me Button" type="checkbox"/>Stop At ClickMeBtn ?</label></td></tr>
</table>
<div>
</td>
<td>
<div id="divTrace" style="height:300; width:300; border:1 red solid;">
<textarea id="traceArea" detail="TextArea" style="width:100%; height:100%"></textarea>
</div>
</td>
</tr></table>
</body>
</html>
本文详细解析了事件传播的过程,包括捕捉阶段、到达事件目标阶段和事件起泡阶段。通过代码示例展示了如何在不同浏览器(如Chrome和IE)中使用addEventListener和attachEvent函数给节点对象添加和删除事件处理函数,并模拟了不同配置项对事件传播过程的影响。

被折叠的 条评论
为什么被折叠?



