最近在看《JavaScript高级程序设计》(第3版),一直没有记录的习惯。现在把一些东西记下来,加深下印象。(基本是照着书码出来的- -!)
一、HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JS代码。如,
<input type="button" value="Click Me" onclick="alert('Clicked')" />
由于这个值是JS代码,因此不能在其中使用未经转义的HTML语法字符。如& "" < >。
在HTML中定义的事件处理程序也可以调用在页面其他地方定义的脚本。如,
<script type="text/javascript">
function showMsg(){
//代码
}
</script>
<input type="button" value="Click Me" onclick="showMsg()" />
HTML事件处理程序会创建一个封装着元素属性值的函数,这个函数中有一个局部变量event,即事件本身,不必定义它,也不用从函数参数列表中读取。如,
<input type="button" value="Click Me" onclick="alert(event.type)" />
在函数内部,this值等于事件的目标元素,如
<input type="button" value="Click Me" onclick="alert(this.value)" />
在HTML中指定事件处理程序主要有三个缺点:
1. 存在时差问题。用户可能会在HTML元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件。若前面的showMsg()函数是在按钮下方、页面的最底部定义的 ,如果用户在页面解析该函数前就单击了按钮,就会引发错误。为此,可以使用try-catch块。如,
<input type="button" value="Click Me" onclick="try{showMsg();}catch(ex){}" />
2. 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同JS引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
3. HTML与JS代码紧密耦合,不便于维护。这应该是主要的缺点了,几乎没人愿意用这方法了吧。
二、DOM0级事件处理程序
通过JS指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。至今仍被所有现代浏览器支持。简单,且具有跨浏览器优势。
首先应取得一个要操作的对象的引用。如,
var btn=document.getElementById("myBtn");
btn.onclick=function(){
//代码
};
注意如果以上代码在页面中位于按钮后面,有可能在一段时间内怎么单击都没反应(这里是指,比如文档要载入的文件很多,而以上代码放在按钮后面,在执行到这些代码之前,该按钮是不会绑定该事件的,即怎么单击都没反应)。在这里有一点要注意的是,要取得一个对象的引用,必须在该对象被解析之后。看以下代码:
<head>
<script type="text/javascript>
var btn=document.getElementById("myBtn");
btn.onclick=function(){
//代码
};
</script>
</head>
<body>
<input type="button" id="myBtn" value="Click Me" />
</body>
单击按钮并没有任何反应。这是因为var btn=document.getElementById("myBtn");并没有取得对象的引用,此时<body>里面的<input>还未被解析。这时要利用window.onload:
<head>
<script type="text/javascript">
window.onload=function(){
//包含上面的JS代码
}
</script>
</head>
window.onload在文档所有元素加载完后执行,这样便能取得对象的引用。当然,将<script>标签放在后面也可以,如,
<body>
<input type="button" id="myBtn" value="Click Me" />
<script type="text/javascript>
var btn=document.getElementById("myBtn");
btn.onclick=function(){
//代码
};
</script>
</body>
结果跟用window.onload一样。
通过DOM0级添加的事件处理程序被认为是元素的方法,即函数内的this引用当前元素,可以通过this访问元素的任何属性和方法。而要删除事件处理程序,则可以如下这样(在HTML指定的事件也如此),
btn.onclick=null;
三、DOM2级事件处理程序
DOM2级事件定义了两个方法:addEventListener()和removeEventListener()。所以DOM节点都包含这俩方法,它们接受3个参数,见代码如下,
var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false
);
最后的参数false表示在冒泡阶段调用该程序。
使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。如,
var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false
);
btn.addEventListener("click",function(){
alert("hello world");
},false
);
这两个事件处理程序会按照添加它们的顺序触发。
通过addEventListener()添加的事件处理程序只能通过removeEventListener()来移除,且参数要相同,意味着添加的匿名函数无法移除。可以用下面的方法,
var btn=document.getElementById("myBtn");
var handler=function(){
//代码
};
btn.addEventListener("click",handler,false);
btn.addEventListener("click",handler,false);
IE9、FF、Safari、Chrome和Opera支持DOM2级事件处理程序。
四、IE事件处理程序
IE实现了与DOM类似的两个方法:attachEvent()和detachEvent()。由前者添加的事件只能由后者来移除,且参数要相同,同样添加的匿名函数无法移除,具体方法见上面代码。
attachEvent()与addEventListener()一样,可以为一个元素添加多个事件,不同的是attachEvent()添加的事件是以相反的顺序被触发的。要注意的是,IE8及更早版本只支持事件冒泡,没有第三个参数。且参数中的事件处理程序名称有前缀on-,与DOM2级不同,如下,
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
//代码
});
在IE中使用attachEvent()与使用DOM方法的主要区别在于事件处理程序的作用域。在DOM方法下,事件处理程序会在其所属元素的作用域中运行;而使用attachEvent(),则会在全局作用域中运行,即this等于window。编写跨浏览器代码时,牢记这一点很重要。
五、跨浏览器的事件处理程序
var EventUtil={
addHandler:function(element,type,handler)
if(element.addEventListener){ //检测是否存在DOM2级方法
element.addEventListener(type,handler,false);
} else if(element.attachEvent){ //IE中的方法
element.attachEvent("on"+type,handler);
} else{ //DOM0级方法
element["on"+type]=handler;
}
},
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;
}
}
};
可以像下面这样使用EventUtil对象:
var btn=document.getElementById("myBtn");
var handler=function(){
//代码
};
EventUtil.addHandler(btn,"click",handler);
EventUtil.removeHandler(btn,"click",handler);
以上没有考虑所有的浏览器问题,如在IE中的作用域问题。但用来添加和移除事件处理程序还是足够的。