邹洋的博客

Don't be the same, be better !

zouyang0921's avatar zouyang0921

JavaScript:事件流 & 事件处理程序

定义:
事件:用户或浏览器自身执行的某种动作。
事件流:描述的是从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。
事件处理程序:响应某个事件的函数就叫做事件处理程序(或事件侦听器)。

事件流

(1)事件冒泡

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

IE9、Firefox、Chrome 和 Safari 则将事件一直冒泡到 window 对象。

(2)事件捕获

  事件捕获的思想与事件冒泡的思想几乎完全相反,是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。如下图所示:

(3)DOM事件流

  “DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段 和 事件冒泡阶段。

  • 首先发生的是事件捕获,为截获事件提供了机会。
  • 然后是实际的目标接收到事件。
  • 最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

  在 DOM 事件流中,实际的目标(

元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从 document 到 再到 后就停止了。下一个阶段是“处于目标”阶段,于是事件在
上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。

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

事件处理程序

  响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以 “on” 开头,因此click 事件的事件处理程序就是 onclick,load 事件的事件处理程序就是 onload 。为事件指定处理程序的方式有以下几种:

(1)HTML事件处理程序

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

缺点:

  • 存在时差问题:因为用户可能会在HTML 元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件,就会引发错误。
  • 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同 JavaScript引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
  • HTML 与 JavaScript 代码紧密耦合。如果要更换事件处理程序,就要改动两个地方:HTML 代码和 JavaScript 代码。

(2)DOM0级事件处理程序

  即将一个函数赋值给一个事件处理程序属性。

var btn = document.getElementById('myBtn');
btn.onclick = function() {
    alert('Clicked');
};
btn.onclick = null; // 删除事件处理程序

优点:

  • 简单
  • 可跨浏览器

(3)DOM2级事件处理程序

addEventListener(事件名称, 事件函数, 是否捕获);        // 添加事件处理程序
removeEventListener(事件名称, 事件函数, 是否捕获);     // 移除事件处理程序

所有 DOM 节点中都包含这两个方法。

优点:可以添加多个事件处理程序。

  通过 addEventListener() 添加的事件处理程序只能使用 removeEventListener() 来移除。所以通过 addEventListener() 添加的匿名函数将无法移除。

  大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,不建议在事件捕获阶段注册事件处理程序。

(4)IE事件处理程序

attachEvent(事件名称, 事件函数);    // 添加事件处理程序
detachEvent(事件名称, 事件函数);    // 移除事件处理程序

由于 IE8 及更早版本只支持事件冒泡,所以通过 attachEvent() 添加的事件处理程序都会被添加到冒泡阶段。

注:例如 添加点击事件,attachEvent() 的第一个参数是 “onclick”,而非 DOM 的 addEventListener() 方法中的 “click” 。

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

  attachEvent() 也可以用来为一个元素添加多个事件处理程序。不过,与 DOM 方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。例如:下面的例子,点击按钮,首先看到的是 “Hello world!”,然后是 “Clicked”。

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function() {
    alert('Clicked');
});
btn.attachEvent('onclick', function() {
    alert('Hello world!');
});

(5)跨浏览器的事件处理程序

  首先要创建的方法是 addHandler() ,它的职责是视情况分别使用 DOM0 级方法、DOM2 级方法或 IE 方法来添加事件。这个方法属于一个名叫 EventUtil 的对象。
  addHandler() 方法接受 3 个参数:要操作的元素、事件名称和事件函数。

var EventUtil = {    
    addHandler: function(element, type, handler) {            // 添加事件
        if (element.addEventListener) { 
            element.addEventListener(type,handler,false);      // 使用DOM2级方法添加事件
        } else if (element.attachEvent) {                   // 使用IE方法添加事件
            element.attachEvent("on"+type,handler);
        } else {
            element["on"+type]=handler;                        // 使用DOM0级方法添加事件
        }
    },  
    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() {
    alert('Clicked');
};
EventUtil.addHandler(btn, 'click', handler);
EventUtil.removeHandler(btn, 'click', handler);