js中的事件
早期的事件流的两种解决方案:
1.IE:事件冒泡流
即事件开始时由最具体的元素(DOM中层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)–自下向上。
所有现代的浏览器都支持事件冒泡。
2.Netscape Communicator:事件捕获流。
与IE团队提出的事件冒泡流刚好相反。由不太具体的节点到具体的节点传播。
这么做的目的:在事件到达预定的目标之前捕获它。
目前主流的浏览器都支持事件流模型,但是老的浏览器可能不支持。
所以,请大胆放心使用事件冒泡模型吧。
一、DOM事件流
“DOM2级事件”规定事件流的三个阶段:
1.事件捕获阶段
2.处理目标阶段
3.事件冒泡阶段
【图13-3】
注意:
1.尽管”DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但是主流浏览器都会在事件捕获阶段触发事件对象上的事件。结果,就是有2个机会在目标对象上面操作事件。
2.IE8之前不支持DOM事件流。
二、事件处理程序
说白了,就是响应事件的函数。一般以’on’开头,如onload、onclick等。
添加事件处理程序的集中的几种方法:
1.HTML事件处理程序
某个dom元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。
如:
type="button" value="Click Me" onclick="alert('Clicked')" />
或者:
<script type="text/javascript">
function showMessage(){
alert("Hello world!");
}
script>
<input type="button" value="Click Me" onclick="showMessage()" />
这样指定事件处理程序,会创建一个封装着元素属性值的函数(函数中的this就代表中当前的dom元素),这个函数中有一个局部变量event,也就是事件对象。
type="button" value="Click Me" onclick="alert(event.type)">
type="button" value="Click Me" onclick="alert(this.value)">
关于这个动态创建的函数,更有意思的是它的扩展作用域的方式,它是这么干的:
function(){
with(document){
with(this){
//元素的属性值
}
}
}
这么干的话,让访问元素自己的属性就变得简单多了。
type="button" value="Click Me" onclick="alert(value)">
当该元素是表单元素的话,则作用域还会包含访问表单元素(当前元素的父元素)的入口:
function(){
with(document){
with(this.form){
with(this){
//元素属性
}
}
}
}
这样扩展作用域的方式,无非就是想让事件处理程序无需引用表单元素就能访问其他表单字段。
这种添加事件处理方式的缺点:
<1>.存在时差:
如果事件处理函数写在元素的下面,可能元素先被解析出来,而事件处理程序对应的函数还没有被加载出来,此时,若是响应事件会出错的。
<2>.这种扩展事件处理程序的作用域的方式在不同的浏览器中实现是不一样的。
<3>.HTML代码与Js代码紧密耦合。
2.DOM0级事件处理程序
每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写,例如:onclick。
var btn = document.getElementById('myBtn');
btn.onclick = function(){
alert(this.id);
}
使用DOM0级事件指定的事件处理程序被认为是元素的方法,所以事件处理程序中的this指的是当前元素,可以通过this访问该元素的其他属性。即事件在当前元素的作用域中运行。
删除事件处理程序:
btn.onclick = null;
3.DOM2级事件处理程序
addEventListener()
removeEventListener()
该两个函数接受三个参数:事件名、事件处理函数、布尔值(true表示事件在捕获阶段调用事件处理程序,false,表示在冒泡阶段调用事件处理程序)
var btn = document.getElementById('myBtn');
btn.addEventListener('click',function(){
alert(this.id);//this仍表示当前触发事件的元素
},false);
可以为一个元素添加多个事件处理程序,事件按照添加事件处理程序的顺序响应。
removeEventListener():移除事件,第二个参数必须和addEventListener()的第二个参数,这意味着事件处理函数不能是匿名函数,否则不能移除。
关于第三个参数,强烈建议设置为false,即在冒泡阶段处理事件,为了最大限度兼容各大浏览器。
4.IE事件处理程序
提供了两个方法添加和移除事件处理程序:
attachEvent()
detachEvent()
第一个参数是事件处理程序名(”on”+事件名),第二个参数是事件处理程序。
var btn = document.getElementById('myBtn');
btn.attachEvent('onclick',function(){
alert('click');
});
注意:
和DOM0级事件和DOM2级事件所不同的是,IE事件的处理程序不是运行在元素的作用域中,而是运行在全局作用域中。即IE指定的事件处理程序中this表示的window对象。
可以为一个元素添加多个事件处理程序,但是执行的顺序是倒着执行的,即最后添加的事件处理程序最先执行。
移除事件处理程序:
detachEvent()
匿名的事件处理程序不能移除。
5.跨浏览器的事件处理程序
其实就是检测各个浏览器支持的事件类型。默认采用DOM0级方法。
var EventUtil = {
addHandler:function(element, type, handler){
if(element.addEventListener){//判断是否支持DOM2级事件处理
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type] = handler;//默认采用DOM0级方法。
}
},
removeHandler:function(element, type, handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type] = null;
}
}
};
注意:
<1>这里只是简单考虑了对元素的添加和移除事件处理程序,并没有考虑其他的问题,如,事件处理程序运行作用域的问题。
<2>DOM级事件只支持添加一个事件处理程序,即你多次调用这个方法为元素添加多个事件处理程序,结果,只会添加最后一个。不过,现在只支持DOM0的浏览器好像也不多了,所以一般不会走到最后一个else。