一、事件流

  1. 事件流:  

  事件流描述的是从页面中接收事件的顺序。IE 的事件流是事件冒泡流,Netscape 的事件流是事件捕获流

  

  2. 事件冒泡流:

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

  若点击了页面中的某个div,则该click事件会按如图所示的顺序传播

前端的事件流以及事件处理程序-风君雪科技博客

   click首先在 div 元素上发生,而这个元素是我们点击的元素。之后,click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。

  3. 事件捕获流

  Netscape 的事件流叫事件捕获,事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。

  在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树依次向下,一直传播到事件的具体目标,即 div 元素。捕获的顺序如图所示

前端的事件流以及事件处理程序-风君雪科技博客

  4. DOM 事件流

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

前端的事件流以及事件处理程序-风君雪科技博客

二、事件处理程序

   响应某个事件的函数就叫事件处理程序(事件侦听器)。事件处理程序的名字以‘on’开头,所以click的事件处理程序是onclick,load的事件处理程序是onload。为事件制定处理程序有如下几种方式:

  (1)HTML 事件处理程序

1 <input type="button" onclick="alert('hello world')">
2 <input type="button" onclick="handle()">
3 <script>
4     function handle() {
5         alert('hello world');
6     }
7 </script>

  (2)DOM0级事件处理程序

1 var btn = document.getElementById('btn');
2 btn.onclick = function() {
3     alert('hello world');
4 }

  使用DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,此时事件处理程序实在元素的作用域中运行,即程序中的this引用当前元素。例如:

1 var btn = document.getElementById('btn');
2 btn.onclick = function() {
3     alert(this.id);    //'btn'
4 }

  在事件处理程序中可以通过 this 访问元素的任何属性和方法,以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

  删除通过 DOM0 级方法指定的事件处理程序,可将事件处理程序的属性值设为 null。

1 btn.onclick = null;

  (3)DOM2 级事件处理程序

  DOM2 级事件有两个方法用于处理指定和删除事件处理程序的操作: addEventListener()removeEventListener()

  所有的 DOM 节点都包含这两个方法,并且都接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。

  一般地,都是把事件处理程序添加都事件流的冒泡阶段,可以极大限度地兼容各种浏览器。最好只在需要在事件到达目标之前捕获它的时候将事件处理程序添加到捕获阶段。除非特殊需要,不建议在事件捕获阶段注册事件处理程序。

1 var btn = document.getElementById('btn');
2 btn.addEventListener('click', function() {
3     alert('hello world');
4 }, false);
5 // 或者
6 function handle() {
7     alert('hello world');
8 }
9 btn.addEventListener('click', handle, false);

   使用 DOM2 级方法添加事件处理程序的好处在于可以添加多个事件处理程序,并且两个事件处理程序会按照添加他们的顺序依次出发。例如:

1 btn.addEventListener('click', function() {
2     alert('hello world');
3 }, false);
4 btn.addEventListener('click', function() {
5     alert(this.id);
6 }, false);
7 //'hello world'
8 //'btn'

   通过 addEventListener() 添加的事件处理程序只能通过 removeEventListener() 移除,移除时传入的参数与添加处理程序使用的参数相同,这意味着通过addEventListener() 添加的匿名函数将无法移除。例如:

1 btn.removeEventListener('click', function() {
2     alert('hello world');
3 }, false);
4 // 无效

  正确的用法是

1 function handler() {
2     alert('hello world');
3 }
4 btn.addEventListener('click', handler, false);
5 btn.removeEventListener('click', handler, false);

   (4)IE事件处理程序

  在 IE 中提供了两个方法 attachEvent() 和 detachEvent()用于添加事件处理程序,都接受两个参数:事件处理程序名称和事件处理程序函数。由于 IE8 及更早版本只支持事件冒泡,因此addtachEvent 添加的事件处理程序都会被添加到冒泡阶段。用法如下:

1 var btn = document.getElementById('btn');
2 btn.attachEvent('onclick', function() {
3     alert('hello world');
4 })

  在使用 attachEvent() 的时候,事件处理程序是运行在全局作用域下,因此 this 指向的是 window 对象。

1 btn.attachEvent('onclick', function() {
2     alert(this == window);    // true
3 })

  attachEvent() 也可以为一个元素添加多个事件处理程序,只不过触发的顺序与添加的顺序相反。

1 btn.attachEvent('onclick', function() {
2     alert(this.id);
3 })
4 btn.attachEvent('onclick', function() {
5     alert('hello world');
6 })
7 // 'hello world'
8 // 'btn'

  detachEvent 用于移除事件处理程序,需要的参数与 attachEvent() 添加时的一样,这也意味着添加的匿名函数不能被移除,正确用法如下:

function handler() {
2     alert('hello world');
3 }
4 btn.attachEvent('onclick', handler);
5 btn.detachEvent('onclick', handler);

三、跨浏览器的事件处理程序

 1 var EventUtil = {
 2     addHandler: function(element, type, handler) {
 3         if(element.addEventListener) {
 4             element.addEventListener(type, handler, false);
 5         }
 6         else if(element.attachEvent) {
 7             element.attachEvent('on' + type, handler);
 8         }
 9         else {
10             element['on' + type] = handler;
11         }
12     },
13     removeHandler: function(element, type, handler) {
14         if(element.removeEventListener) {
15             element.removeEventListener(type, handler, false);
16         }
17         else if(element.detachEvent) {
18             element.detachEvent('on' + type, handler);
19         }
20         else {
21             element['on' + type] = null;
22         }
23     }
24 }
25 // 使用方法
26 var btn = document.getElementById('btn');
27 function handlerFn() {
28     alert('hello world');
29 }
30 EventUtil.addHandler(btn, click, handlerFn);
31 EventUtil.removeHandler(btn, click, handlerFn);