本文深入解析JavaScript中为元素指定事件处理程序的五种方式。从基础的HTML内联处理到DOM0级、DOM2级(addEventListener)、IE专有方法(attachEvent),最后介绍封装好的跨浏览器事件处理工具。详细对比了各方式在作用域、执行顺序、移除机制及浏览器兼容性上的差异,帮助开发者根据实际需求选择最优方案。
JavaScript事件处理概述
本文详细解析了JavaScript中为事件指定处理程序的五种具体实现方式,旨在为开发者提供清晰的参考与对比。
JavaScript与HTML之间的交互机制主要依赖于事件系统。
目前,IE9、Firefox、Opera、Safari以及Chrome等主流浏览器均已实现了DOM2级事件模块的核心功能,而IE8则是最后一个仍依赖其专有事件系统的主要浏览器版本。
事件流机制:
事件流描述了页面接收事件的顺序。历史上,IE与Netscape提出了截然相反的事件流概念:IE采用事件冒泡流,而Netscape采用事件捕获流。
1) 事件冒泡机制
事件冒泡是指事件从最具体的元素(即文档中嵌套层次最深的节点)开始接收,随后逐级向上传播至较不具体的节点(最终到达文档对象)。
需要注意的是,blur、focus、load和unload这几个事件不支持冒泡机制。
2) 事件捕获机制
在事件捕获模式下,不太具体的节点会先接收到事件,而最具体的目标节点最后接收。其设计意图是在事件到达预定目标之前将其捕获。
尽管IE9、Safari、Chrome、Firefox和Opera均同时支持事件捕获和冒泡,但IE8及更早版本仅支持冒泡而不支持捕获。因此,建议优先使用事件冒泡,仅在特殊需求下才使用事件捕获。
DOM事件流结构:
DOM2级事件规范规定的事件流包含三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。理论上,在捕获阶段目标节点不会接收事件,事件仅在目标阶段于目标节点上发生,且目标阶段在逻辑上被视为冒泡阶段的一部分。然而,IE9、Safari、Chrome、Firefox以及Opera 9.5及以上版本在捕获阶段也会触发目标对象上的事件,导致目标对象有两次机会处理事件。
IE9、Firefox、Opera、Safari和Chrome支持完整的DOM事件流,而IE8及更早版本不支持DOM事件流。
事件处理程序定义:
事件是指用户操作或浏览器自身触发的动作,而响应这些事件的函数称为事件处理程序(或事件侦听器),其命名通常以“on”开头。
JavaScript中主要存在五种指定事件处理程序的方式:
HTML与DOM0级事件处理
1) HTML事件处理程序
每种事件都可以通过一个与处理程序同名的HTML特性来指定,该特性的值可以是可执行的JavaScript代码或函数名。在函数内部,存在一个局部变量event用于访问事件对象,且this关键字指向事件的目标元素。
在HTML中直接指定事件处理程序存在以下主要缺点:
① 时差问题:用户可能在HTML元素渲染完成前就触发事件,此时事件处理程序可能尚未准备好。为解决此问题,许多HTML事件处理程序被封装在try-catch块中,以捕获并隐藏错误,避免用户看到报错信息。
② 作用域链差异:不同浏览器中扩展事件处理程序的作用域链会导致不同的执行结果。由于各JavaScript引擎的标识符解析规则存在细微差异,访问非限定对象成员时容易出错。
③ 代码耦合度高:HTML代码与JavaScript代码紧密耦合,若需更换事件处理程序,必须同时修改HTML和JavaScript代码。
2) DOM0级事件处理程序
这是通过JavaScript指定事件处理程序的传统方式,即将一个函数赋值给元素的事件处理程序属性。使用DOM0级方法指定的处理程序被视为元素的方法,因此this引用当前操作元素。
DOM0级事件处理程序具有以下优势:
① 实现简单;
② 具备良好的跨浏览器兼容性。
可以通过将事件处理程序属性设置为null来删除通过DOM0级方法指定的处理程序。
DOM2级与IE事件处理机制
3) DOM2级事件处理程序
DOM2级事件定义了两个用于指定和删除事件处理程序的方法:addEventListener()和removeEventListener()。这两个方法均接收三个参数:事件名、处理函数以及一个布尔值(true表示在捕获阶段调用,false表示在冒泡阶段调用)。
DOM2级事件处理程序的主要优势在于:
可以为目标元素添加多个事件处理程序,这些程序将严格按照添加顺序依次触发。
通过addEventListener()添加的处理程序只能使用removeEventListener()移除,且移除时传入的参数必须与添加时完全一致。因此,通过addEventListener()添加的匿名函数无法被移除,必须向removeEventListener()传入addEventListener()中定义的命名函数才能正常移除。
4) IE事件处理程序
IE事件模型提供了两个方法:attachEvent()和detachEvent(),它们均接受两个参数:事件名称及事件处理函数。鉴于IE8及更早版本仅支持事件冒泡,通过attachEvent()注册的处理程序均执行于冒泡阶段。
注意:通过IE的attachEvent()添加的处理程序名称以“on”开头,而通过DOM的addEventListener()添加的名称不包含“on”前缀。
在IE中使用attachEvent()与DOM0级方法的主要区别在于:
作用域不同。在IE中使用时,处理程序在全局作用域中运行,此时this指向window;而使用DOM0级方法时,处理程序在所属元素的作用域中运行。
在IE中使用attachEvent()与DOM2级方法的区别在于:
多个处理程序的执行顺序不同。在IE中使用添加多个处理程序时,它们按添加的相反顺序触发;而在DOM中使用addEventListener()添加多个处理程序时,它们按添加顺序触发。
通过attachEvent()添加的处理程序只能使用detachEvent()移除,且要求移除参数与添加参数相同。因此,通过attachEvent()添加的匿名函数无法移除,需向detachEvent()传入attachEvent()中命名的函数方可正常移除。
跨浏览器事件处理方案
5) 跨浏览器的事件处理程序
为确保事件处理代码在大多数浏览器中一致运行,只需关注冒泡阶段即可。
根据具体情况分别调用DOM2级方法、IE方法或DOM0级方法来添加和移除事件。addHandler()和removeHandler()方法封装在EventUtil对象中。
① 首先检测传入元素是否具备DOM2级方法(传入第三个参数false以指定冒泡阶段);
② 其次检测传入元素是否具备IE专有方法;
③ 最后检测传入元素是否具备DOM0级方法(使用方括号语法将属性名指定为事件处理程序)。
var EventUtil = {
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;
},
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;
}
}
PS:关于JavaScript事件的详细说明,可参考本站提供的javascript事件与功能说明大全:http://tools.jb51.net/table/javascript_event
更多关于JavaScript相关内容的精彩文章,建议读者查阅本站专题:《JavaScript事件相关操作与技巧大全》、《JavaScript页面元素操作技巧总结》、《JavaScript操作DOM技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript错误与调试技巧总结》。
希望本文所述的五种方式能对大家掌握JavaScript程序设计有所帮助。