DOM2和IE的事件传播机制(捕捉,起泡)

 

当事件发生在某个文档节点上时(即事件目标),目标的事件处理程序就会被触发。此外目标的每个祖先节点也有机会处理该事件。
2级DOM的事件传播包含三个阶段:

  1. 捕捉阶段(capturing),事件从顶级文档树节点一级一级向下遍历,直到到达该事件的目标节点。
  2. 到达事件的目标节点,执行目标节点的时间处理程序。
  3. 事件起泡(bubbling),事件从目标节点一级一级向上上溯,直到顶级文档树节点。

相应的,2级DOM通过下面的两个函数给节点对象添加和删除事件处理函数。
      addEventListener(eventType, handler, propagate);
      removeEventListener(eventType, handler, propagate);
三个参数意思分别如下:

  1. eventType: 即事件类型(不加on)。比如:"click"。
  2. handler: 事件处理函数。传入参数即为事件对象event。
  3. 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" οnclick="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>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值