为了更好地解析事件流,首先看一下示例文件的效果:
效果演示
示例文件flow.html包含嵌套的列表和一些JavaScript和CSS文件:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Event Flow</title> <link rel="stylesheet" type="text/css" href="../styles/source.css" /> <link rel="stylesheet" type="text/css" href="style.css" /> <script type="text/javascript" src="../libs/ADS-final-verbose.js"></script> <script type="text/javascript" src="../libs/myLogger.js"></script> <script type="text/javascript" src="flow.js"></script> </head> <body> <h1>Event Flow</h1> <div id="content"> <ul id="list1"> <li> <p>List 1 </p> <ul id="list2"> <li> <p>List 2 </p> <ul id="list3"> <li> <p>List 3 </p> </li> </ul> </li> </ul> </li> </ul> <ul id="list4"> <p>List 4 </p> </ul> </div> </body> </html>
使用CSS样式设置第3个列表(List 3)的位置,使它的位置处于祖先元素之外,且位于第4个列表(List 4)之上。这个设置是要关注的重点,因为即使List 3浮动到了List 4的上面,它仍然嵌套在其祖先元素中:
#list1 {
height:80px;
}
#list2 {
margin-top: 10px;
height:20px;
}
#list3 {
position:absolute;
top:190px;
left:150px;
}
#list4 {
margin-top:10px;
height:100px;
}
[/code]
flow.js文件为每个无序列表都注册了click事件侦听器,主要任务是修改被单击元素的类样式:
ADS.addEvent(window,'load',function() {
// 使用经过修改的addEvent()方法
function modifiedAddEvent( obj, type, fn ) {
if(obj.addEventListener) {
// W3C方式
obj.addEventListener( type, fn, true );
} else if ( obj.attachEvent ) {
// IE方式
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else {
return false;
}
}
var counter = 0;
// 取得无序列表
var lists = document.getElementsByTagName('ul');
for(var i = 0 ; i < lists.length ; i++ ) {
// 注册事件侦听器
modifiedAddEvent(lists[i],'click',function() {
// 向段落中添加表示先后顺序的数字
var append = document.createTextNode(':' + counter++);
this.getElementsByTagName('p')[0].appendChild(append) ;
// 修改被单击元素的样式
this.className = 'clicked';
});
}
});
[/code]
查看了效果演示并在浏览器中单击List 3,会发现List 1和List 2也改变了颜色,而这与List 3的位置无关,只与元素的嵌套关系有关。
<a href="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow1.jpg"><img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow1.jpg" alt="blog_flow1" width="550" height="231" class="alignnone size-full wp-image-4721" /></a>
1. 事件的顺序
注意到上图在调用事件侦听器之后,出现在列表名称后面的数字。这些数字表示的是事件被处理的顺序,0表示首先被处理。如果看到的数字和图中的不一致,且数字顺序相反,则说明使用的不是与DOM2级事件规范兼容的浏览器。这也在于示例中修改了addEvent()方法,以不同的方式注册click事件侦听器:
function modifiedAddEvent( obj, type, fn ) {
if(obj.addEventListener) {
// W3C方式
obj.addEventListener( type, fn, true );
} else if ( obj.attachEvent ) {
// IE方式
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else {
return false;
}
}
修改后的函数将addEventListener()方法的最后一个参数替换成true。即启用了事件流的捕获阶段,而禁用了冒泡阶段。当事件被注册在捕获阶段,浏览器会先从最外层的祖先元素开始触发click事件,然后向内部依次传递。
然而,IE不支持捕获,其attachEvent()方法只能按照从目标元素向外传播到祖先元素的顺序。如下图所示:
2. 两个阶段和三个模型
事件冒泡和事件捕获恰好是相反的过程。由IE提出的事件冒泡,指的是目标元素的事件首先触发,然后事件向外传播到每个祖先组员,直至document对象。考虑下面这个非常简单的文档:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Sample Markup</title>
</head>
<body>
<ul>
<li>
<a href="http://example.com">Link 1</a>
</li>
<li>
<a href="http://example.com">Link 2</a>
</li>
</ul>
</body>
</html>
单击Link 1的冒泡事件流如下图所示:
由Netscape提出的事件捕获,则把优先权赋予最外层的祖先元素,事件向内传播,直至目标元素。如下图所示:
在所有支持W3C DOM的浏览器中,冒泡事件流是默认事件流。事实上,W3C事件规范中并没有提出不同的事件流,但却通过同时包含捕获和冒泡阶段较好地解决了这两个极端的问题。如下图所示
3. 阻止冒泡
要在W3C DOM2级事件中取消冒泡阶段,可以简单地在事件对象上调用stopPropagation()方法。同样,对于IE浏览器需要将事件的cancelBubble属性设置为true。
为了弥合这种差异性,在自定义的ADS库中添加ADS.stopPropagation()方法:
/**
* 阻止事件的传播
*/
function stopPropagation(eventObject) {
eventObject = eventObject || getEventObject(eventObject);
if(eventObject.stopPropagation) {
eventObject.stopPropagation();
} else {
eventObject.cancelBubble = true;
}
}
window['ADS']['stopPropagation'] = stopPropagation;
4.取消默认操作
与事件流有关的最后一个问题是默认动作。例如可以通过使用由click事件侦听器返回的false值,阻止浏览器重定向到href属性所指向的页面。虽然这种方式也有效,但正确的方式还是要根据浏览器的能力而进行处理。DOM2事件规范使用了preventDefault()方法取消默认动作,IE将事件对象的retureValue属性设置为false来取消默认事件。
在自定义的ADS库中添加ADS.preventDefault()方法:
/**
* 阻止浏览器的默认行为
*/
function preventDefault(eventObject) {
eventObject = eventObject || getEventObject(eventObject);
if(eventObject.preventDefault) {
eventObject.preventDefault();
} else {
eventObject.returnValue = false;
}
}
window['ADS']['preventDefault'] = preventDefault;
转载请注明:陈童的博客 » ADS4.2 响应用户操作和事件——事件流