ADS4.2 响应用户操作和事件——事件流
为了更好地解析事件流,首先看一下示例文件的效果:效果演示
示例文件flow.html包含嵌套的列表和一些JavaScript和CSS文件:
[code lang="html"]
Event Flow
Event Flow
-
List 1
-
List 2
-
List 3
-
-
List 4
[/code]
使用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事件侦听器,主要任务是修改被单击元素的类样式:
[code lang="js"]
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的位置无关,只与元素的嵌套关系有关。
<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" />
1. 事件的顺序
注意到上图在调用事件侦听器之后,出现在列表名称后面的数字。这些数字表示的是事件被处理的顺序,0表示首先被处理。如果看到的数字和图中的不一致,且数字顺序相反,则说明使用的不是与DOM2级事件规范兼容的浏览器。这也在于示例中修改了addEvent()方法,以不同的方式注册click事件侦听器:
[code lang="js"]
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;
}
}
[/code]
修改后的函数将addEventListener()方法的最后一个参数替换成true。即启用了事件流的捕获阶段,而禁用了冒泡阶段。当事件被注册在捕获阶段,浏览器会先从最外层的祖先元素开始触发click事件,然后向内部依次传递。
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow2.jpg" alt="blog_flow2" width="550" height="240" class="alignnone size-full wp-image-4729" />
然而,IE不支持捕获,其attachEvent()方法只能按照从目标元素向外传播到祖先元素的顺序。如下图所示:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow3.jpg" alt="blog_flow3" width="550" height="241" class="alignnone size-full wp-image-4725" />
2. 两个阶段和三个模型
事件冒泡和事件捕获恰好是相反的过程。由IE提出的事件冒泡,指的是目标元素的事件首先触发,然后事件向外传播到每个祖先组员,直至document对象。考虑下面这个非常简单的文档:
[code lang="html"]
Sample Markup
[/code]
单击Link 1的冒泡事件流如下图所示:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow4.jpg" alt="blog_flow4" width="550" height="112" class="alignnone size-full wp-image-4734" />
由Netscape提出的事件捕获,则把优先权赋予最外层的祖先元素,事件向内传播,直至目标元素。如下图所示:
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow5.jpg" alt="blog_flow5" width="550" height="112" class="alignnone size-full wp-image-4736" />
在所有支持W3C DOM的浏览器中,冒泡事件流是默认事件流。事实上,W3C事件规范中并没有提出不同的事件流,但却通过同时包含捕获和冒泡阶段较好地解决了这两个极端的问题。如下图所示
<img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_flow6.jpg" alt="blog_flow6" width="550" height="129" class="alignnone size-full wp-image-4735" />
3. 阻止冒泡
要在W3C DOM2级事件中取消冒泡阶段,可以简单地在事件对象上调用stopPropagation()方法。同样,对于IE浏览器需要将事件的cancelBubble属性设置为true。
为了弥合这种差异性,在自定义的ADS库中添加ADS.stopPropagation()方法:
[code lang="js"]
/**
* 阻止事件的传播
*/
function stopPropagation(eventObject) {
eventObject = eventObject || getEventObject(eventObject);
if(eventObject.stopPropagation) {
eventObject.stopPropagation();
} else {
eventObject.cancelBubble = true;
}
}
window['ADS']['stopPropagation'] = stopPropagation;
[/code]
4.取消默认操作
与事件流有关的最后一个问题是默认动作。例如可以通过使用由click事件侦听器返回的false值,阻止浏览器重定向到href属性所指向的页面。虽然这种方式也有效,但正确的方式还是要根据浏览器的能力而进行处理。DOM2事件规范使用了preventDefault()方法取消默认动作,IE将事件对象的retureValue属性设置为false来取消默认事件。
在自定义的ADS库中添加ADS.preventDefault()方法:
[code lang="js"]
/**
* 阻止浏览器的默认行为
*/
function preventDefault(eventObject) {
eventObject = eventObject || getEventObject(eventObject);
if(eventObject.preventDefault) {
eventObject.preventDefault();
} else {
eventObject.returnValue = false;
}
}
window['ADS']['preventDefault'] = preventDefault;
[/code]
查看完整版本: ADS4.2 响应用户操作和事件——事件流
Tags: