欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript事件处理机制

程序员文章站 2022-05-05 22:56:27
...

1 事件流

背景:当浏览器发展到第四代时,浏览器开发团队遇到一个很有意思的问题:页面的那一部分会调用

定义:事件流描述的是从页面中接收事件的顺序。IE和Netscape开发团队提出了完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。

1.1 事件冒泡

IE的事件流叫做叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Event Bubbking Example</title>
</head>
<body>
    <div id="myDiv"> Click me</div>
</body>
</html>

click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。如图:

JavaScript事件处理机制

兼容性:

(1)IE5.5及更早版本中的事件冒泡会跳过<html>元素(从<body>直接跳到document)

(2)IE9、FireFox、Chrome和Safari则将事件一直冒泡到window对象

1.2 事件捕获

Netscape Communicator团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

以事件冒泡的代码为例,在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树一次向下,一直传播到事件的实际目标,即<div>元素。

JavaScript事件处理机制

兼容性:由于老版本的浏览器不支持,因此很少人使用事件捕获。我们应该放心地使用事件冒泡,在有特殊需要时再使用事件捕获。

1.3 DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段式冒泡阶段,可以在这个阶段对事件做出响应。

JavaScript事件处理机制

兼容性:

(1)多数支持DOM事件流的浏览器都实现了一种特定的行为;即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的时间。结果,就是有两个机会在目标对象上面操作事件。

(2)IE9、Safari、Chrome、Firefox和Opera都支持DOM事件流;IE8及更早版本不支持DOM事件流。

2 事件处理程序

定义:事件就是用户或浏览器自身执行的某种动作。诸如click、load和mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以“on”开头。

事件指定处理程序的方式:HTML事件处理程序、DOM0级事件处理程序、DOM2级事件处理程序、IE事件处理程序、跨浏览器的事件处理程序

2.1 HTML事件处理程序

某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JavaScript代码。

<input type="button" value="Click Me" onclick="alert('Clicked')" />

缺点:

(1)存在一个时差问题。因为用户可能会在HTML元素一出现在页面行就触发相应的事件,但当时的事件处理程序有可能尚未不具备执行条件。

(2)扩展事件处理程序的作用链在不同浏览器中会导致不同结果。不同JavaScript引擎遵循的标识符解析规则略有差异,很可能在访问非限定对象成员时出错。

(3)HTML与JavaScript代码紧密耦合,如果更改事件处理程序,就要改动两个地方:HMTL代码和JavaScript代码。

2.2 DOM0级事件处理程序

摒弃HTML事件处理程序而使用JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种事件处理程序赋值的方法是在第四代Web浏览器中出现。

优点:

(1)简单(2)具有跨浏览器的优势

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Clicked");
}

使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行的。

2.3 DOM2级事件处理程序

定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点都包含这个两个方法,并且接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是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()来移除;移除时传入的参数与添加处理程序时的使用的参数相同,这意味着通过addEventListener()添加的匿名函数将无法移除。

var btn = document.getElementById("myBtn");
btn.addEventListener("click", funtion(){
    alert(this.id);
},false);

//这里省略了其他代码

btn.removeEventListener("click", funtion(){    //没有用
    alert(this.id);
},false);

 

var btn = document.getElementById("myBtn");
var handler = funtion(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);

//这里省略了其他代码

btn.removeEventListener("click", handler, false);    //有效

大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。

兼容性:IE9、Firefox、Safari、Chrome和Opera支持DOM2级事件处理程序。

2.4 IE事件处理程序

IE实现了与DOM类似的两个方法:attachEvent()和detachEvent()。接受相同的两个参数:事件处理程序名称与事件处理程序函数。

与DOM事件处理程序的区别:

(1)在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。

(2)attachEvent()方法可以用来为一个元素添加多个事件处理程序,但不是以添加它们的顺序执行,而是以相反的顺序被触发。

var btn = document.getElementById("btn");
btn.attachEvent("onclick", function () {
    alert("Clicked");
});

兼容性:只支持IE和Opera。注:经过自己试验,IE11不支持。

2.5 跨浏览器的事件处理程序

方法:使用能够隔离浏览器差异的JavaScript库(如jQuery),还有是自己开发最合适的事件处理的方法。

要保证处理事件的代码能在大多数浏览器下一致地运行,只需关注冒泡阶段。

        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;
              }
          }
        };
        var btn = document.getElementById("btn");
        var handler = function () {
            alert("Clicked");
        }
        EventUtil.addHandler(btn, "click", handler);