理解javascript中的事件模型

 

 javascript中有两种事件模型:DOM0,DOM2。而对于这两种的时间模型,我一直不是很是的清楚,如今经过网上查阅资料终于明白了一些。javascript

  

  一.  DOM0级事件模型html

  DOM0级事件模型是早期的事件模型,全部的浏览器都是支持的,并且其实现也是比较简单。代码以下:java

<p id = 'click'>click me</p>
<script>
    document.getElementById('click').onclick = function(event){
        alert(event.target);
    }
</script>

  这种事件模型就是直接在dom对象上注册事件名称,这段代码就是在p标签上注册了一个onclick事件,在这个事件函数内部输出点击的目标。而解除事件则更加简单,就是将null复制给事件函数,以下:浏览器

document.getElementById('click'_).onclick = null;

  由此咱们能够知道dom0中,一个dom对象只能注册一个同类型的函数,由于注册多个同类型的函数的话,就会发生覆盖,以前注册的函数就会无效。dom

复制代码
var click = document.getElementById('click');
click.onclick = function(){
    alert('you click the first function');
};
click.onclick = function(){
    alert('you click the second function')
}
复制代码

  在这段代码中,咱们为dom对象注册了两个onclick函数,可是结果是只执行了第二个注册的函数,前面所注册的函数被覆盖了。函数

 

  二.   DOM2级事件模型post

  1.  事件捕获和事件冒泡(capture,bubble)ui

  首先,IE8及如下是不支持这种事件模型的。事件捕获和事件冒泡的机制以下图:this

  如上图所示,123表明事件捕获,4567表明事件冒泡。首先咱们使用下面的代码:lua

<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>

  假设咱们点击了ID为inner的div,那么此时的事件流程就是,首先执行捕获阶段:document-html-body-div(outer)。而后执行冒泡阶段:div(inner)-div(outer)-body-html-document。

  2.   DOM2级的注册事件和解除事件

  在DOM2级中使用addEventListener和removeEventListener来注册和解除事件(IE8及以前版本不支持)。这种函数较之以前的方法好处是一个dom对象能够注册多个相同类型的事件,不会发生事件的覆盖,会依次的执行各个事件函数。

  addEventListener('事件名称','事件回调','捕获/冒泡')。示例以下:

复制代码
<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
    var click = document.getElementById('inner');
    click.addEventListener('click',function(){
        alert('click one');
    },false);
    click.addEventListener('click',function(){
        alert('click two');
    },false);
</script>
复制代码

  首先咱们要知道addEventListenr的第一个参数是事件名称,与DOM0级不一样的是没有”on“,另外第三个参数表明捕获仍是冒泡,true表明捕获事件,false表明冒泡事件。

  而在这段代码中,咱们为inner的div注册了两个click事件函数,结果是浏览器会依次执行这两个函数。

  下面咱们演示如何使用事件流的发生机制。

复制代码
<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(){
        alert('inner show');
    },true);
    clickouter.addEventListener('click',function(){
        alert('outer show');
    },true);
</script>
复制代码

  这段代码,咱们使用了捕获事件,因为inner是嵌套在outer中的,因此咱们知道当使用捕获的时候outer是应该首先捕获到这个事件的,其次inner才能捕获到这个事件。那么结果就是outer首先执行,其次是inner执行。

  那么我把outer的执行时机改成冒泡的阶段呢?

alickouter.addEventListener('click',function(){
  alert('outer show');  
},false);

  这种状况下,就是先执行inner后执行outer了。同理咱们把两者的事件执行时机都改成冒泡阶段的话,依旧是先执行inner后执行outer。那么还有个问题,就是若是咱们把inner注册两个click事件,一个是在捕获阶段,另外一个是在冒泡阶段,也就是说把addEventListenter的第三个参数分别设置为false和true,那么执行的顺序又是怎样的呢。

复制代码
<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(){
        alert('capture show');
    },true);
    click.addEventListener('click',function(){
        alert('bubble show');
    },false);
</script>
复制代码

  这种状况下首先这些的是capture show,其次是bubble show。可是这种结果是与注册的顺序有关系的,先注册就先执行。由于咱们在看事件捕获和事件冒泡示意图,发现最后具体的dom对象是只有一个的。

  那么 若是咱们给outer和inner都注册了click事件可是我不但愿outer执行怎么办呢?这个时候咱们就须要用到stopPropagation函数了,这个函数是用来阻止冒泡,言下之意是让事件再也不继续冒泡下去,这样接下来注册一样类型事件的dom对象就不会执行了。好比在自制下拉框的时候,咱们点击浏览器的其余位置,咱们须要下拉框的options隐藏,这时咱们就要用到stopPropagation了。以下:

复制代码
<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(event){
        alert('inner show');
        event.stopPropagation();
    },false);
    clickouter.addEventListener('click',function(){
        alert('outer show');
    },false);
</script>
复制代码

  正常的状况下,咱们在不添加stopPropagation函数时,首先应该执行inner,而后执行outer,可是当咱们在inner的事件函数中添加了stopPropagation函数以后,执行完inner的事件函数以后,就不会在执行outer的事件函数了,也能够理解为事件冒泡到inner以后就消失了,所以也就不会在执行接下来的事件函数了。

  因为事件捕获阶段没有能够阻止事件的函数,因此通常都是设置为事件冒泡。

Javascript与HTML之间的交互是经过事件实现的。
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
可使用侦听器来预约事件,以便事件发生时执行相应代码。

事件流

事件流描述的是从页面中接受事件的顺序。

事件冒泡

IE的事件流叫作事件冒泡,即事件开始是由最具体的元素接收,而后逐级向上传播到较为不具体的节点(文档)。
若是点击了页面中的一个div元素,那么这个click事件可能会按照以下顺序传播:

  • < div>
  • < body>
  • < html>
  • document

也就是说,click事件首先在div元素上发生,而后click事件沿DOM树向上传播,在每一级节点上都会发生,直至传播至document对象。

这里写图片描述

全部现代浏览器都支持冒泡事件。

事件捕获

Netscape团队提出的另外一种事件流叫作事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,最具体的节点应该最后接收到事件。
事件捕获的用意在于事件到达预约目标以前捕获它。

这时,单击div元素就会如下列顺序触发click事件:

  • document
  • < html>
  • < body>
  • < div>

事件捕获过程当中,document对象首先接受click事件,而后事件沿DOM树依次向下,一直传播到事件的实际目标,即div元素。

这里写图片描述

虽然大多数浏览器都支持事件捕获,但不多有人使用。

DOM事件流

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

  • 首先发生的是事件捕获,为截获事件提供了机会。
  • 而后是实际的目标接收到事件。
  • 而后冒泡阶段发生,事件又传播回文档。

这里写图片描述

在DOM事件流中,实际的目标在捕获阶段不会接受到事件。这意味着在捕获阶段,事件从document到< html>再到< body>后就中止了。
下一个阶段是“处于目标”阶段,因而事件在div元素上发生,并在事件处理中被当作冒泡阶段的一部分。

即便DOM2级事件明确要求捕获阶段不会涉及事件目标,但大多数浏览器都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上面操做事件。

事件处理程序

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

HTML事件处理程序

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

<input type="button" value="click me" onclick="alert('clicked')"/>
  • 1

当单击这个按钮时,就会显示一个警告框。这个操做是经过指定onclick特性并将一些javascript代码做为它的值来定义的。因为这个值是javascript,所以不能在其中使用未经转义的HTML语法字符。

在HTML中定义的事件处理程序能够包含要执行的具体动做,也能够调用在页面其余地方定义的脚本。

<script type="text/javascript"> function showMessage(){ alert("hello world") } </script>
<input type="button" value="click me" onclick="showMessage()" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这个例子中,单击按钮就会调用showMessage函数。这个函数是在一个独立的< script>元素中定义的,固然也能够被包含在一个外部文件中。事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码。

这样指定事件处理程序具备一些独到之处。
首先,这样会建立一个封装着元素属性值的函数。
这个函数中有一个局部变量event,也就是事件对象。

<input type="button" value="click me" onclick="alert(event.type)" />
  • 1

经过event变量,能够直接访问事件对象。

在这个函数内部,this值等于事件的目标元素。

<input type="button" value="click me" onclick="alert(this.value)" />
  • 1

关于这个动态建立的函数,另外一个有意思的地方就是它扩展做用域的方式。这个函数使用with像下面这样扩展做用域:

function(){
    with(document){
        with(this){
            //元素属性值
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如此一来,事件处理程序能够直接访问本身的属性:

<input type="button" value="click me" onclick="alert(value)" />
  • 1

若是当前元素是一个表单输入元素,则做用域中还会包含访问表单元素的入口。

function(){
    with(document){
        with(this.form){
            with(this){
                //元素属性值
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这样的扩展方式,无非就是想让事件处理程序无需引用表单元素就能访问其余表单字段。

<form method="post">
    <input type="text" name="username" value="">
    <input type="button" value="echo username" onclick="alert(username.value)">
</form>
  • 1
  • 2
  • 3
  • 4

注意,这个例子中直接引用了username元素。

可是,在HTML中指定事件处理程序有两个缺点:

  • 存在一个时差问题。用户可能在HTML元素一出如今页面上就触发相应的事件,但当时的事件处理程序有可能尚不具有执行条件。
    之前面的例子来讲明,假设showMessage函数是在按钮下方,页面的最底部定义的。若是用户在页面解析showMessage( )以前就单击了按钮,就会引起错误。为此,不少HTML事件处理程序都会被封装到一个try-catch块中,一遍错误不会浮出水面。

  • 这样扩展事件处理程序的做用域链在不一样浏览器中会致使不一样结果。

  • HTML代码与javascript代码紧密耦合。若是要更换事件处理程序,就要改动两个地方:HTML代码和javascript代码。这正是你们摒弃HTML事件处理程序,转而使用javascript指定事件处理程序的缘由所在。

DOM0级事件处理程序

经过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
每一个元素(包括window和document)都有本身的事件处理程序属性,这些属性一般所有小写,例如onclick。将这种属性的值设置为一个函数,就能够指定事件处理程序:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("clicked");
}
  • 1
  • 2
  • 3
  • 4

在此,咱们经过文档对象取得了一个按钮的引用,而后为它指定了onclick事件处理程序。
但要注意,在这些代码运行之前不会指定事件处理程序,所以若是这些代码在页面中位于按钮后面,就有可能在一段时间内怎么单击都没有反应。

使用DOM0级方法指定的事件处理程序被认为是元素的方法。所以,这时候的事件处理程序是在元素的做用域中运行;换句话说,程序中的this引用当前元素。

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

不只仅是id,实际上能够在事件处理程序中经过this访问元素的任何属性和方法。
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

也能够删除经过DOM0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null便可:

btn.onclick = null;  //删除事件处理程序
  • 1

将事件处理程序设置为null后,再单击按钮将不会有任何动做发生。

若是使用HTML指定事件处理程序,那么onclick属性的值就是一个包含着在同名HTML特性中指定的代码的函数。将相应的属性设为null,也能够删除以这种方式指定的事件处理程序。

DOM2级事件处理程序

DOM2级事件定义了2个方法:

  • addEventListener()
  • removeEventListener()

全部DOM节点中都包含这两个方法,而且它们都接受3个参数:

  • 要处理的事件名
  • 做为事件处理程序的函数
  • 一个布尔值
    • true:表示在捕获阶段调用事件处理程序
    • false:表示在冒泡阶段调用事件处理程序

要在按钮上为click事件添加事件处理程序,可使用下列代码:

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
    alert(this.id);
},false);
  • 1
  • 2
  • 3
  • 4

上面的代码为一个按钮添加了onclick事件处理程序,并且该事件会在冒泡阶段被触发。
与DOM0级方法同样,这里添加的事件处理程序也是在其依附的元素的做用域中运行,所以this表明该元素。

使用DOM2级方法添加事件处理程序的主要好处是能够添加多个事件处理程序。

btn.addEventListener("click",function(){
    alert(this.id);
},false);
btn.addEventListener("click",function(){
    alert("hello world");
},false);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里为按钮添加了两个事件处理程序,这两个事件处理程序会按照添加他们的顺序触发,所以首先会显示元素的ID,其次会显示“hello world”消息。

经过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着经过addEventListener()添加的匿名函数将没法移除。

btn.addEventListener("click",function(){
    alert(this.id);
},false);

btn.removeEventListener("click",function(){ //没有用
    alert(this.id);
},false);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这个例子中,在移除事件处理程序时,看似使用了相同的参数,但实际上第二个参数与传入addEventListener()中的是彻底不一样的函数
传入removeEventListener()中的事件处理程序函数必须与传入addEventListener()中的彻底相同

var handler = function(){
    alert(this.id);
}
btn.addEventListener("click",handler false);

btn.removeEventListener("click",handler ,false); //有效!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

重写后的这个列子能够奏效,由于在addEventListener()和removeEventListener()中使用了相同的函数。

大多数状况下,都是将事件处理程序添加到事件流的冒泡阶段,这样能够最大限度的兼容各类浏览器。最好只在须要在事件到达目标以前截获他的时候将事件处理程序添加到捕获阶段。若是不是特别须要,不建议在事件捕获阶段注册时间处理程序。

IE事件处理程序

IE实现了与DOM中相似的链各个方法:attachEvent()和detachEvent()。
这两个方法接受相同的两个参数,事件处理程序名称与事件处理程序函数。
因为IE8及更早版本只支持事件冒泡,因此经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。

要使用attachEvent()为按钮添加一个事件处理程序,可使用如下代码:

btn.attachEvent("onclick",function(){
    alert("hello world");
});
  • 1
  • 2
  • 3

注意,attachEvent()的第一个参数是“onclick”,而非DOM方法中的“click”。

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

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

与addEventListener()相似,attachEvent()方法也能够用来为一个元素添加多个事件处理程序。

btn.attachEvent("onclick",function(){
    alert("clicked"); 
});
btn.attachEvent("onclick",function(){
    alert("hello world"); 
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用attachEvent为同一个按钮添加了两个不一样的事件处理程序。但与DOM事件不一样的是,这些事件处理程序不是以添加他们的顺序执行,而是以相反的顺序被触发。 单击这个按钮,首先会看到“hello world”,而后才是“clicked”。

使用attachEvent()添加的事件能够经过detachEvent()来移除,条件是必须提供相同的参数。这也意味着添加的匿名函数将不能被移除。不过,只要可以将对相同函数的引用传给detachEvent(),就能够移除相应的事件处理程序。

var handler = function(){
    alert(this.id);
}
btn.attachEvent("onclick",handler );
btn.detachEvent("onclick",handler );
  • 1
  • 2
  • 3
  • 4
  • 5

这个例子将保存在变量handler中的函数做为事件处理程序。所以,后面的detachEvent()可使用相同的函数来移除事件处理程序。

支持IE事件处理程序的浏览器有IE和opera。

跨浏览器的事件处理程序

能够本身编写跨浏览器的事件处理程序,只要在适当的地方使用能力检测便可。要保证处理事件的代码能在大多数浏览器下一致地运行,只需关心冒泡阶段。
第一个要建立的方法是addHandler(),他的职责是视状况分别使用DOM0级方法、DOM2级方法或IE方法来添加事件。这个方法属于一个名叫EventUtil的对象,咱们将使用这个对象来处理浏览器间的差别。
addHandler接收3个参数:要操做的元素、事件名称和事件处理函数。

与addHandler()对应的方法是removeHandler(),它也接受相同的参数。这个方法的职责是移除以前添加的事件处理程序——不管该事件处理程序是采起什么方式添加到元素中的,若是其它方法无效,默认采用DOM0级方法。

var EventUtil = {
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListner(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.removeEventListner(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on"+type,handler);
        }else{
            element["on"+type] = null;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这两个方法首先都会检测传入的元素中是否存在DOM2级方法。若是存在DOM2级方法,则使用该方法;若是存在的是IE的方法,则采起第二种方案。最后一种可能就是DOM0级方法,此时咱们使用的是方括号语法来将属性名指定为事件处理程序,或者将属性设置为null。

能够像下面这样使用EventUtil对象:

var btn = document.getElementById("myBtn");
var handler = function(){
    alert(this.id);
}
EventUtil.addHandler(btn,"click",handler);
EventUtil.removeHandler(btn,"click",handler);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

addHandler()和removeHandler()没有考虑到全部的浏览器问题,例如IE中的做用域问题。不过,使用它们添加和移除事件处理程序仍是足够了。
此外还要注意,DOM0级对每一个事件只支持一个事件处理程序。

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着全部与事件有关的信息。包括致使事件的元素、事件的类型以及其余与特定事件相关的信息。
全部浏览器都支持event对象,但支持方式不一样。

DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。不管指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。

btn.onclick = function(event){
    alert(event.type);  //"click"
}
btn.addEventListener("click",function(event){
    alert(event.type);  //"click"
},false);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这个例子中的两个事件处理程序都会弹出一个警告框,显示由event.type属性表示的事件类型。这个属性始终都会包含被触发的事件类型。(与传入addEventListener()和removeEventListener()的事件类型一致)。

在经过HTML特性指定事件处理程序时,变量event中保存着event对象。

<input type="button" value="click me" onclick="alert(event.type)" />
  • 1

以这种方式提供event对象,可让HTML特性事件处理程序与javascript函数执行相同的操做。

event对象包含与建立它的特定事件有关的属性和方法。触发的事件类型不同,可用的属性和方法也不同。

currentTarget、target

target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是同样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(通常为父级)。

换句话说,event.target指向引发触发事件的元素,而event.currentTarget则是事件绑定的元素

在事件处理程序内部,对象this始终等于currentTarget的值,而target只包含事件的实际目标。若是直接将事件处理程序指定给了目标元素,则this、currentTarget、target包含相同的值。

btn.onclick = function(event){
    alert(event.currentTarget === this);  //true
    alert(event.target === this);  //true
}
  • 1
  • 2
  • 3
  • 4

因为click事件的目标是按钮,所以这三个值是相等的。
若是事件处理程序存在于按钮的父节点中,那么这些值是不相同的:

document.body.onclick = function(event){
    alert(event.currentTarget === document.body);  //true
    alert(this === document.body);  //true
    alert(event.target === document.getElementById("myBtn"));  //true
}
  • 1
  • 2
  • 3
  • 4
  • 5

当单击这个例子中的按钮时,this和currentTarget都等于document.body,由于事件处理程序是注册到这个元素上的。然而target元素却等于按钮元素,由于它是click的真正目标。因为按钮上没有注册事件处理程序,结果click事件就冒泡到了document.body上了,在那里事件才获得了处理。

type

在须要经过一个函数处理多个事件时,可使用type属性。

var handler = function(event){
    switch(event.type){
        case "click":
            alert("clicked");
            break;
        case "mouseover":
            event.target.style,backgroundColor = "red";
            break;
        case "mouseout":
            event.target.style.backgroundColor = "";
            break;
    }
};

btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这个例子定义了一个名为handler的函数,用于处理3种事件:click、mouseover、mouseout。函数中经过检测event.type属性,让函数可以肯定发生了什么事件,并执行相应的操做。

preventDefault()

要阻止特定事件的默认行为,可使用这个方法。例如,连接的默认行为是被单击时会导航到其href特性指定的URL。若是想阻止这一默认行为,那么经过链接的onclick事件处理程序能够取消它。

link.onclick = function(event){
    event.preventDefault();
}
  • 1
  • 2
  • 3

只有cancelable属性设为true的事件,才可使用preventDefault()来取消其默认行为。

stopPropagation()

用于当即中止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。
例如,直接添加到一个按钮的事件处理程序能够调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序。

btn.onclick = function(event){
    alert("clicked");
    event.stopPropagation();
}
document.body.onclick = function(event){
    alert("body clicked");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对于这个例子而言,若是不调用stopPropagation(),就会在单击按钮时出现两个警告框。使用了以后,因为click事件根本不会被传播到document.body,所以就不会触发注册在document.body上的onclick事件程序。

eventPhase

这个属性能够用来肯定事件当前正位于事件流的哪一个阶段。

  • 捕获阶段:eventPhase = 1;
  • 处于目标对象上:eventPhase = 2;
  • 冒泡阶段:eventPhase = 3;

要注意的是,尽管“处于目标”发生在冒泡阶段,可是eventPhase仍等于2.

btn.onclick = function(event){
    alert(event.eventPhase);  //2
}
document.body.addEventListener("click",function(event){
    alert(event.eventPhase);  //1
},true);
document.body.onclick = function(event){
    alert(event.eventPhase);  //3
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当单击这个例子中的按钮时:

  • 首先执行的事件处理程序是在捕获阶段触发的添加到document.body中的那一个,会弹出一个警告框显示1
  • 其次会触发在按钮上注册的事件处理程序,此时eventPhase为2
  • 最后在冒泡阶段触发添加到document.body中的那一个,显示eventPhase为3。

当eventPhase等于2时,this、target、currentTarget始终都是相等的。

只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。

IE中的事件对象

与访问DOM中的event对象不一样,要访问IE中的event对象有几种不一样的方式,取决于指定事件处理程序的方法。

  • 在使用DOM0级方法添加事件处理程序时,event对象做为window随想的一个属性存在:
btn.onclick = function(){
    var event = window.event;
    alert(event.type); //"click"
}
  • 1
  • 2
  • 3
  • 4
  • 若是事件处理程序是经过attachEvent()添加的,那么就会有一个event对象做为参数被传入事件处理程序函数中:
btn.attachEvent("onclick",function(event){
    alert(event.type);   //"click"
})
  • 1
  • 2
  • 3

在这种状况下,也能够经过window对象来访问event对象,就像使用DOM0级方法同样。不过为了方便起见,同一个对象也会做为参数传递。

  • 若是是经过HTML特性指定的事件处理程序,那么还能够经过一个名为event的变量来访问event对象(与DOM中的事件模型相同)。
<input type="button" value="click me" onclick="alert(event.type)"/>
  • 1

IE的event对象一样也包含与建立它的事件相关的属性和方法。其中不少属性和方法都有对应的或者相关的DOM属性和方法。

srcElement

事件的目标,与DOM中的target属性相同。
由于事件处理程序的做用域是根据指定它的方式来肯定的,因此不能认为this会始终等于事件目标,所以最好仍是使用event.srcElement比较保险。

btn.onclick = function(){
    alert(window.event.srcElement == this);  //true
}

btn.attachEvent("onclick",function(event){
    alert(event.srcElement == this);  //false
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第一个使用DOM0级方法指定的事件处理程序,srcElement属性等于this。
但在第二个事件处理程序中,因为this指向window,所以这二者的值不相同。

returnValue

默认值为true,将其设置为false就能够取消事件的默认行为,与DOM中的preventDefault()做用相同。

link.onclick = function(){
    window.event.returnValue = false;
}
  • 1
  • 2
  • 3

cancelBubble

与DOM中的stopPropagation()相同,都是用来中止事件冒泡的。因为IE不支持事件捕获,所以只能取消事件冒泡。但stopPropagation()能够同时取消捕获和冒泡。

btn.onclick = function(){
    alert("clicked");
    window.event.cancelBubble = true;
}

document.body.onclick = function(){
    alert("body clicked");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

经过在onclick事件处理程序中将cancelBubble设置为true,就能够阻止时间经过冒泡而触发document.body中注册的事件处理程序。结果就是,单击按钮以后,只显示一个警告框。

跨浏览器的事件对象

虽然DOM和IE中的event对象不一样,但基于它们之间的类似性依旧能够拿出跨浏览器的方案来。
能够对前面的EventUtil对象加以加强:

var EventUtil = {
    getEvent:function(event){
        return event?event:window.event;
    },
    getTarget:function(event){
        return event.target||event.srcElement;
    },
    preventDefault:function(event){
        if(event.preventDefault){
            event.preventDefault();
        }else{
            event.returnValue = false;
        }
    },
    stopPropagation:function(event){
        if(event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble = true;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

能够像下面这样使用:

btn.onclick = function(event){
    event = EventUtil.getEvent();
    var target = EventUtil.getTarget(event);
    EventUtil.preventDefault(event);
    EventUtil.stopPropagation(event);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

别忘了因为IE不支持事件捕获,所以stopPropagation在跨浏览器的状况下,只能用来阻止事件冒泡。

相关文章
相关标签/搜索