本文系统总结了JavaScript中常见的事件处理程序实现方式,涵盖HTML内联指定、DOM0级、DOM2级以及IE特有方法。重点解析了各方法的作用域差异、事件触发阶段及移除机制,并提供了基于能力检测的跨浏览器事件处理工具函数,帮助开发者编写兼容现代与旧版浏览器的代码。
JavaScript事件处理程序概述
本文通过具体实例,系统梳理了JavaScript中常见的事件处理程序用法,旨在为开发者提供实用的参考与借鉴。
事件是指由用户操作或浏览器自身触发的动作,例如点击行为。用于响应这些事件的函数被称为事件处理程序,也可称为事件监听器。其命名通常以“on”作为前缀,例如处理点击事件的处理程序名为 onclick。
HTML 内联事件处理程序
1. HTML 事件处理程序
若某元素支持特定事件,则必然存在一个与该事件处理程序同名的 HTML 属性。开发者可通过该属性直接指定 JavaScript 代码。
<input type="button" value="点我" onclick="alert('点击过咯')"/>
由于脚本代码嵌入在 HTML 元素的属性值中,因此代码字符串需使用单引号包裹。
此外,也可以调用页面其他位置定义的脚本函数。
<input type="button" value="Click me" onclick="showMessage()">
<script type="text/javascript">
function showMessage() {
console.log("Hello World!");
}
</script>
在此类事件处理程序中,代码执行时能够访问全局作用域内的任何变量与函数。
这种指定方式会动态创建一个封装了元素属性值的函数,该函数内部包含一个局部变量 event,即事件对象。
<input type="button" value="点我" onclick="alert(event.type)"/>
借助 event 变量,可直接访问事件对象的属性与方法。在该函数内部,this 关键字指向触发事件的目标元素。
<input type="button" value="点我" onclick="alert(this.value)"/>
可通过动态创建的函数来扩展其作用域链。在该函数内部,可以访问 document 对象以及该元素自身的成员,其作用域扩展方式类似于使用 with 语句。
function(){
with(document){
with(this){
//元素属性值
}
}
}
因此,在事件处理程序中访问元素自身的属性变得十分便捷。
<!-- 输出 “点我” -->
<input type="button" value="点我" onclick="alert(value)"/>
若当前元素为表单内的输入元素,则作用域链中还会包含指向父级表单元素的入口,使得函数结构更为复杂。
function(){
with(document){
with(this.form){
with(this){
//元素属性值
}
}
}
}
这种机制使得事件处理程序无需显式引用表单元素,即可直接访问表单中的其他字段。
<form method="post">
<input type="text" name="username" value="">
<input type="button" value="Echo Usernmame" onclick="console.log(username.value);">
</form>
在 HTML 中直接指定事件处理程序存在以下局限性:
- 存在时差问题。若函数定义在页面底部,而用户在页面解析完成前点击按钮,将引发错误。因此,许多 HTML 事件处理程序会被封装在 try-catch 块中以增强容错性。
<input type="button" value="点我" onclick="try{alert(value);} catch(ex){}"/>
- 扩展事件处理程序的作用域链在不同浏览器中的表现可能不一致。
- HTML 与 JavaScript 代码高度耦合。修改事件处理程序需同时更改两处代码,因此许多开发人员倾向于使用 JavaScript 代码来指定事件处理程序。
DOM0 级事件处理程序
2. DOM0 级事件处理程序
该方法通过将函数赋值给事件处理程序属性来指定处理逻辑。使用此方法前,必须先获取目标对象的引用。
每个元素(包括 window 和 document)均拥有自己的事件处理程序属性,通常采用小写命名,如 onclick。将属性值设为函数,即完成了事件处理程序的指定。
DOM0 级事件处理程序作为元素的方法运行,其执行上下文为元素自身,因此代码中的 this 引用当前操作元素。
<button id="myBtn">点我</button>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function () {
console.log(this.id);//myBtn
}
</script>
可通过 this 访问元素的任意属性与方法。DOM0 级事件处理程序通常在事件流的冒泡阶段被触发。
可通过将属性值设为 null 来移除以此方式指定的事件处理程序。
btn.onclick = null; //删除指定的事件处理程序
注意:若使用 HTML 属性指定事件处理程序,onclick 属性的值即为包含同名 HTML 特性中代码的函数。将相应属性设为 null,同样可删除此类事件处理程序。
DOM2 级事件处理程序
3. DOM2 级事件处理程序
- addEventListener():用于指定事件处理程序。
- removeEventListener():用于删除事件处理程序。
所有 DOM 节点均包含这两个方法,它们接受三个参数:事件名、处理程序函数以及布尔值。若第三个参数为 true,表示在捕获阶段调用处理程序;若为 false,表示在冒泡阶段调用。
DOM2 级事件处理程序支持便捷地添加多个事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function () {
console.log(this.id);
}, false);
btn.addEventListener("click", function () {
console.log("Hello world!");
}, false);
这些处理程序将严格按照添加顺序依次触发。
通过 addEventListener() 添加的事件处理程序,只能使用 removeEventListener() 移除。移除时传入的参数必须与添加时完全一致。这意味着通过 addEventListener() 添加的匿名函数将无法被移除。
var handler = function () {
console.log("Hi deniro");
};
btn.addEventListener("click", handler, false);
btn.removeEventListener("click", handler, false);//移除成功
大多数情况下,建议将事件处理程序添加到事件流的冒泡阶段,以最大化浏览器兼容性。除非有特殊需求,否则避免在事件捕获阶段注册处理程序。
注意:IE9、Firefox、Safari、Chrome 和 Opera 均支持 DOM2 级事件处理程序。
IE 特有事件处理程序
4. IE 事件处理程序
IE 实现了与 DOM 类似的方法:attachEvent() 和 detachEvent()。它们接受两个参数:事件处理程序名称和处理程序函数。由于 IE8 及更早版本仅支持事件冒泡,使用 attachEvent() 添加的处理程序会被置于冒泡阶段。
以下为按钮添加事件处理程序的示例。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function () {
});
IE 的 attachEvent() 与 DOM0 级方法的主要区别在于事件处理程序的作用域不同。在 DOM0 级中,处理程序在其所属元素的作用域内运行;而使用 attachEvent() 时,处理程序在全局作用域中运行,因此 this 指向 window。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function () {
console.log("Clicked");
console.log(this === window);//true;this 为全局作用域
});
同样可以为一个元素添加多个事件处理程序。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function () {
console.log("Clicked");
console.log(this === window);//true;this 为全局作用域
});
//IE10 中多个事件是按照事件定义的顺序执行
btn.attachEvent("onclick", function () {
console.log("Hello world!");
});
IE8 及之前版本以添加顺序的相反顺序执行处理程序,IE9 修复了这一行为。
可通过 detachEvent() 移除添加的事件处理程序,但必须提供相同的参数。因此,它同样无法移除通过匿名函数添加的处理程序。只要能将相同函数的引用作为参数传入,即可成功移除。
<button id="myBtn">点我</button>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
var handler = function () {
console.log("Clicked");
}
btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler);
</script>
注意:IE 和 Opera 支持 IE 事件处理程序。
跨浏览器兼容方案
5. 跨浏览器的事件处理程序
只要恰当运用能力检测,即可编写出跨浏览器兼容的事件处理程序。
var EventUtil = {
/**
* 添加事件
* @param element 要操作的元素
* @param type 事件名称
* @param handler 事件处理函数
*/
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
/**
* 移除事件
* @param element 要操作的元素
* @param type 事件名称
* @param 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;
}
}
}
使用方式如下。
<button id="myBtn">点我</button>
<script type="text/javascript" src="EventUtil.js"/>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
var handler = function () {
console.log("Clicked");
}
EventUtil.addHandler(btn, "click", handler);
EventUtil.removeHandler(btn, "click", handler);
</script>
此外,DOM0 级对每个事件仅支持一个处理程序。好在目前仅支持 DOM0 级的浏览器已几乎绝迹,因此无需过度担忧此限制。
更多关于 JavaScript 相关内容感兴趣的读者可查看本站专题:《JavaScript事件相关操作与技巧大全》、《JavaScript操作DOM技巧总结》、《JavaScript页面元素操作技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript错误与调试技巧总结》。
希望本文所述内容对大家学习 JavaScript 程序设计有所帮助。