事件就是用户或者浏览器自身执行的某种动作。比如click、mouse等,都是事件的名字。而响应某个事件的函数就叫做事件处理程序或者事件监听器。事件处理程序的名字一般是以“on”开头,比如click事件的事件处理程序就是onclick,mouse事件的事件处理程序就是onmouse。那么,我们在给事件指定处理程序的时候,可以采用好几种的方式。
1、HTML事件处理程序
只要是元素可以支持的事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JavaScript代码。比如,给一个按钮添加一个单击事件,代码如下:
<input type="button" value="click me" οnclick="alert('clicked')"/>
在HTML中定义的事件处理程序可以包含要执行的具体工作,也可以调用在页面其他地方定义的脚本,如下所示:
<input type="button" value="click me" οnclick="showMessage()"/>
<script>
function showMessage(){
alert("This is the click event.");
}
</script>
在这个例子中,单击按钮就会调用这个showMessage()函数。当然,我们知道这个函数可以在一个独立的<script>元素中定义,也可以写到一个外部的js文件中,只需要在页面中引入即可。值得注意的是,事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
但是,在HTML中指定事件处理程序有三个缺点。
- 存在一个时差问题
因为用户会在HTML元素一出现在页面时就点击触发相应的事件,但当时的事件处理程序有可能尚不具备执行的条件。比如说我们在前面写的例子,如果页面中出现了按钮,但是我们从外部引进了这个函数,函数尚未加载,那么点击这个事件,就会引发错误。所以,我们可以将这个事件处理程序封装在一个try-catch中,以便错误让用户看不见。如下所示:
<input type="button" value="click me" οnclick="try{alert('clicked');}catch (ex){}"/>
- 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同的结果
- 通过这种方法指定事件处理程序,如果需要更换事件处理程序,那么就需要改动HTML代码和JavaScript代码,因为HTML与JavaScript代码紧密耦合。
2、DOM0级事件处理程序
要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这个时候的事件处理程序是在元素的作用域中运行,换句话说,程序中的this引用当前的元素。
<input id="myBtn" type="button" value="click me"/>
<script>
var myBtn = document.getElementById("myBtn");
myBtn.onclick = function (){
alert(this.id);
}
</script>
单击按钮显示的是元素的id,这个id是通过this.id取得的。当然,我们通过this,不仅可以得到id,而且可以在事件处理程序中通过this访问这个元素的任何属性和方法。注意:以这种方法添加的事件处理程序会在事件流的冒泡阶段被处理。
我们可以通过将事件处理程序设置为null来达到删除DOM0级方法指定的事件处理程序,如下所示:
myBtn.onclick = null ;
3、DOM2级事件处理程序
DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点中都包含这两个方法,并且都接受三个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。注意:这个布尔值如果是true,表示在捕获阶段调用事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序。
我们举一个简单的例子,要在按钮上为click事件添加事件处理程序,可以使用如下的代码:
<input id="myBtn" type="button" value="click me"/>
<script>
var myBtn = document.getElementById("myBtn");
myBtn.addEventListener("click", function () {
alert(this.id);
},false);
</script>
我们把第三个参数设置为false,说明是在冒泡阶段被触发。与DOM0级方法一样,在这里添加的事件处理程序也是在其元素的作用域中执行。使用DOM2级添加事件处理程序的主要好处是可以添加多个事件处理程序。
如下所示:
var myBtn = document.getElementById("myBtn");
myBtn.addEventListener("click", function () {
alert(this.id);
},false);
myBtn.addEventListener("click", function () {
alert("Hello world!");
},false);
注意:这两个事件处理程序会按照添加它们的顺序触发。通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除。移除时传入的参数和添加处理程序时所使用的参数是一样的,也是三个参数。这意味着通过addEventListener()添加的匿名函数无法移除。如下代码所示,我们通过使用addEventListener()添加了一个事件处理程序,虽然调用removeEventListener()时看似使用了相同的参数,但是实际上,第二个参数传入的函数是完全不一样的。下面的例子是无法移除我们添加的事件处理程序。
var myBtn = document.getElementById("myBtn");
myBtn.addEventListener("click", function () {
alert(this.id);
},false);
//……
myBtn.removeEventListener("click", function () {
alert(this.id);
},false);
如果想移除通过addEventListener()添加的匿名函数,那么我们可以采用下面的方法来解决,代码如下:
var myBtn = document.getElementById("myBtn");
var handler = function () {
alert(this.id);
};
myBtn.addEventListener("click", handler ,false);
myBtn.removeEventListener("click", handler ,false);
注意:在大多数情况下,我们都会将事件处理程序添加到事件流的冒泡阶段,因为这样可以最大限度的兼容各种浏览器。
4、IE事件处理程序
IE事件处理程序与DOM2级事件处理程序的方法不一样。在IE事件处理程序中,实现了类似DOM中的两个方法:attachEvent()和detachEvent(),这两个方法接收2个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早版本只支持冒泡事件,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段触发。
我们要在按钮上为click事件添加事件处理程序,可以使用如下的代码:
<input id="myBtn" type="button" value="click me"/>
<script>
var myBtn = document.getElementById("myBtn");
myBtn.attachEvent("onclick", function () {
alert("clicked");
});
</script>
这里需要注意的是,事件处理程序名称中需要添加“on”,与DOM2级事件处理程序addEventListener()添加的不一样。
接下来我们看一段程序,如下所示:
var myBtn = document.getElementById("myBtn");
myBtn.attachEvent("onclick", function () {
alert(this.id);
});
通过试验,我们可以发现在IE中弹出的结果是undefined,而刚才我们在DOM2级中弹出的结果是id。之所以出现这样的结果,是因为在IE中使用与DOM方法的主要区别在于事件处理程序的作用域不同。我们刚才说过,在使用DOM2级addEventListener()方法的情况下,事件处理程序会在其所属元素的作用域内执行,在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中执行,所以刚才的this等于window,我们未给其定义id,自然就无法得到id的值,为了证实这个说法是否成立,我们通过下面的例子看到。
var myBtn = document.getElementById("myBtn");
myBtn.attachEvent("onclick", function () {
alert(this == window); //true
});
与addEventListener()类似,attachEvent()方法也可以用来为一个元素添加多个事件处理程序,但是需要注意的是,通过attachEvent()添加的事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。具体的例子这里就不写了。
使用attachEvent()添加的事件可以通过detachEvent()来移除,条件同样是必须提供相同的参数,与DOM方法一样,添加的匿名函数不能被移除,解决方法与上面的DOM2级方法一样,这里也不写详细的代码。
5、跨浏览器的事件处理程序
刚才我们说过,为了最大限度的兼容各种浏览器,我们只需要关注冒泡阶段。
第一个要创建的方法是addHandler(),这个方法的职责是视情况分别使用DOM0级方法、DOM2级方法或者IE方法来添加事件处理程序。这个方法属于EventUtil的对象。addHandler()方法接受3个参数:要操作的元素、事件名称、事件处理程序函数。
既然有添加函数,那么对应的也有移除函数,这个函数是removeHanlder(),它也接受相同的参数。
EventUtil的用法如下所示:
var EventUtil = {
addHanlder: function (element, type , hanlder) {
if(element.addEventListener){
element.addEventListener(type, hanlder, false);
}else if(element.attachEvent){
element.attachEvent("on" + type, hanlder);
}else{
element["on" + type] = hanlder;
}
},
removeHanlder: function (element, type ,hanlder) {
if(element.removeEventListener){
element.removeEventListener(type, hanlder, false);
}else if(element.detachEvent){
element.detachEvent("on" + type, hanlder);
}else{
element["on" + type] = null;
}
}
};
那我们如何使用这个EventUtil对象呢?如下所示:
var myBtn = document.getElementById("myBtn");
var hanlder = function () {
alert("Hello world");
};
EventUtil.addHanlder(myBtn, click, hanlder);
EventUtil.removeHanlder(myBtn, click, hanlder);
总结几点需要注意的地方:
1)DOM2级方法中事件类型前面不需要添加“on”,而DOM和IE方法的事件类型都必须添加“on”;
2)DOM和IE的作用域不同,DOM的作用是其所属元素的作用域,而IE的作用域是全局作用域;
3)DOM方法中,事件处理程序是按照添加它们的顺序执行,而IE是按照添加它们的顺序的相反顺序执行。