[3]Event对象

  1. Event对戏那个在event第一次出发的事后就被创建出来,并且一直伴随着事件在DOM结构中流转整个生命周期。event对象会被作为第一个参数传递给事件监听的回调函数。event对象具有以下的属性:

type(String) – 事件的名称
target(node) – 事件起源的DOM节点
currentTarget(node) – 当前回调函数被触发的DOM节点
bubbles(boolean) – 指明该事件是否是一个冒泡事件(需要注意的是:不是所有的事件都是可以冒泡的)
cancelable(boolean) – 指明这个事件的默认行为是否可以通过调用preventDefault() 來阻止,也就是只有当cancelable为true的时候,调用preventDefault方法才能生效
eventPhase(number) – 指明当前事件所处的阶段(phase),none(0), capturing(1), target(2), bubbling(3)
timestamp(number) – 事件发生的事件
defaultPrevented(boolean) – 指明当前事件对象都额preventDefault方法是否被调用过
preventDefault(function) – 这个方法将阻止浏览器中用户代理对当前事件的相关默认行为。
stopPropagation(function) – 这个方法将阻止当前事件链上后面的元素的回调函数被触发,当前节点上针对此事件的其他回调函数依然会被触发。
stopImmediatePropagation(function) – 这个方法将阻止当前事件链上所有回调函数被触发,也包括当前针对此事件已绑定的其他回调函数。

具体案例
更改版本案例

[2]事件传播与冒泡

1.事件传播

在web发展的早期,出现了如下的困惑:当我们在网页中点击某一区域的时候,是哪个DOM元素和我们进行相互交互。我们都知道,HTML采用的是树级嵌套结构,当我们点击某一个元素的时候,该元素的父元素同样被点击。可以说任何一个元素被点击,和标签都一样被点击。为了解决这个问题,网景Netscape和微软分别提出了不同的方法。

1.Netscape的解决方法:

  • 当元素事件被激活的时候,事件总是从DOM树的最高层对象开始依次往下。也就是说任何一个元素事件发生,最先开始捕获的事件的是document,然后是html,再接着则是body,依次往下。
  • 网景的这种处理方式称之为事件捕获即Event Capturing.

2.微软Internet Explorer的解决方法
IE的解决方法和网景的截然相反。当一个事件发生的时候,最先发生在绑定该事件的元素上,接着再依次向上冒泡。也就是当一个元素事件发生的时候,最先开始监测到事件的是绑定时间段额元素,接着才是该元素的父元素,依次到body、html,最后到document。
IE的这种事件处理方式称之为事件冒泡即Event Bubble.

后来W3C在定义DOM Level 2 Events的时候,将一个事件发生的过程定义了三个过程,分别是Event Capturing Phase,Event Target Phase,Event Bubble Phase。如下图所示:
事件传播示意图

在DOM Level 0 Events中的两种定义事件方式,在处理事件的时候都是采用事件冒泡的方式进行。而DOM Level 2 Events则可以选择事件是在捕获阶段还是冒泡阶段处理(由addEventListener()函数中的第三个参数决定)。

Example:

1.事件捕获、目标、冒泡三个阶段

1
2
3
4
5
6
7
<body>
<div>
<ul>
<li>Click Me!</li>
</ul>
</div>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var _div = document.querySelector("div");
var _ul = document.querySelector("ul");
var _li = document.querySelector("li");
//Capturing Phase
_div.addEventListener("click", callback, true);
_ul.addEventListener("click", callback, true);
_li.addEventListener("click", callback, true);
//Bubble Phase
_div.addEventListener("click", callback, false);
_ul.addEventListener("click", callback, false);
_li.addEventListener("click", callback, false);
//callback function
function callback(event){
var event = event || window.event;
var _currentTarget = event.currentTarget
alert(_currentTarget + " capturing");
}

具体案例点击查看

2.事件冒泡的使用

事件冒泡有其好处和弊端。在这一小节我们先来看看事件冒泡该如何使用?

当需要对多个元素进行事件绑定的时候,如果仍然采用之前我们所说的方法,则会造成内存负担,同时也会是JavaScript的执行速度越来越慢。如:

1
2
3
4
5
6
7
8
9
document.getElementById("help-btn").onclick = function(event){
openHelp();
};
document.getElementById("save-btn").onclick = function(event){
saveDocument();
};
document.getElementById("undo-btn").onclick = function(event){
undoChanges();
};

如果有100个元素需要绑定事件,按照这种方法我们需要写100个事件绑定,这显然是不现实的。对于这种情况我们应该使用事件代理(Event Delegation,又译为:事件委托)的方式来解决。Event Delegation利用的原理就是事件冒泡Bubbling。

事件代理就是利用事件冒泡在DOM最高层次(通常是document)对事件进行处理。当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。
当事件在父节点处理的时候,我们需要知道的是哪个子节点触发了事件,这可以通过子节点的特性进行判断,如target.id子节点的id,target.className子节点的类名,target.nodeName子节点的名称等。
所以对于上面的例子,我们可以使用事件代理改写成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
document.onclick = function(event){
//IE不会传入event参数
var event = event || window.event;
//IE 使用的是srcElement作为原始事件对象
var target = event.target || event.srcElement;
switch(target.id){
case "help-btn":
openHelp();
break;
case "save-btn":
saveDocument();
break;
case "undo-btn":
undoChanges();
break;
}
}

另外,也可以通过库进行处理。JQuery库就有处理事件代理的相应函数。

3.阻止事件冒泡

现在我们考虑下这种情况,html代码如下:

1
2
3
<div>
<p>Click me!</P>
</div>

在div标签下,我们需要绑定一个点击事件,该事件用于处理div标签,假设需要输出:这是一个DIV标签。
而在p标签,我们同样需要绑定一个点击事件,该事件用于处理P标签,假设需要输出:这是DIV标签下的P标签。
在正常的事件过程中,当点击P之后,事件会向上冒泡,引起div标签的click事件发生。但是这不是我们所要的效果,我们需要的是点击P标签的时候只执行p标签绑定的点击事件,于div绑定的点击事件无关。这时候就需要用到stopPropagation()函数,它能阻止当前元素的事件向上冒泡。
Example点击查看
Example点击查看
第一个例子中没有阻止事件冒泡,第二个例子中添加了阻止事件冒泡的函数stopPropagation();

[1]DOM Level 0 与 DOM Level 2事件

  1. DOM Level 0事件

    • 一开始浏览器处理事件的方式是将事件处理程序被设置为js代码串,且作为html的属性值(DOM Level 0的事件直接绑定到HTML代码中),如:

      1
      <span id="test" onclick="alert('event inline');">Click me!</span>
    • DOM Level 0对某一元素不能同时绑定同一事件,因为后面的事件会覆盖前面同样的事件绑定

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <span id="test" onclick="alert('event inline');">Click me!</span>
      ```
      ```JavaScript
      var test = document.getElementById("test");
      test.onclick = function(){
      alert("event first");
      };

      test.onclick = function(){
      alert("event second");
      }

      Example点击查看
      最后弹框出现的只有event second,event first绑定的事件被覆盖,inline的事件也被覆盖

    • DOM Level 0绑定事件的两种方法如上:
      1.将事件作为html的属性直接写在html代码中。
      2.获取元素,在通过元素的.onEvent = function(){}进行绑定。
  2. DOM Level 2事件

    • DOM Level 2规定了事件的传播方式,主要有三个阶段,分别是捕获Capturing Phase、目标Target Phase和冒泡Bubble Phase。这个知识点详细内容请看目录[2]事件传播与冒泡
    • DOM Level 2的事件则是通过addEventListener(IE浏览器使用attachEvent)函数对元素设置监听函数。
      • addEventListener(String, function(event), Boolean)函数介绍
        1. 第一个参数String,指的是事件类型。如绑定点击事件则是click,需要注意的是没有on前缀,不是onclick
        2. 第二个参数是事件处理函数,JS在调用的时候会传入事件event,关于事件event的详细内容请看目录[3]Event对象
        3. 第三个参数是一个boolean值,取true或者false。当为true的时候则表示在事件Capturing Phase阶段执行处理函数;为false的时候则表示在事件Bubble Phase阶段执行处理函数。
    • 通过DOM Level 2事件绑定,同个元素可以绑定多个相同事件,各个事件根据绑定的顺序依次执行。

      1
      <span id="test" onclick="alert('event inline');">Click me!</span>
      1
      2
      3
      4
      5
      6
      7
      8
      var test = document.getElementById("test");
      test.addEventListener("click", function(){
      alert("event first");
      });

      test.addEventListener("click", function(){
      alert("event second");
      });

      Example点击查看

  3. DOM Level 0 与 DOM Level 2的比较
    1. 事件的绑定方式不同
    2. DOM Level 2将事件分为三个阶段,而DOM Level 0则没有,默认事件在相当于DOM Level 2事件Bubble Phase处理(即如果用0级DOM的2个方法赋值的事件监听函数不能在capturing阶段捕捉到事件)。
    3. DOM Level 0事件浏览器支持比较统一,而如果使用DOM Level 2则在IE和非IE情况下需要做区分。具体的处理方法请看目录[5]跨浏览器中事件