事件流,事件处理程序,事件对象

1.事件流

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

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

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

1.3 DOM事件流

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处理目标程序阶段和事件冒泡阶段。

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

2.事件处理程序

事件就是用户或浏览器自身执行的某种动作。

而响应某个时间的函数就叫做事件处理程序(或事件侦听器)。

2.1 HTML事件处理程序

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

1 <!--Clicked不能用双引号-->
2     <input type="button" value="Click Me" onclick="alert('Clicked')"></input>

在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本,如下代码所示:

1 <script type="text/javascript">
2         function showMessage(){
3             alert("Hello world!");
4         }
5     </script>
6     <input type="button" value="Click Me" onclick="showMessage()" />

在HTML中指定事件处理程序有三个缺点

 (1)时差问题。因为用户可能会在HTML元素一出现在页面上就触发相应的事件,当时的事件处理程序可能不具备执行的条件(此时会引发错误)。为此,很多HTML事件处理程序都会封装在一个try-catch块中,此时错误就可以被捕获了。前面的代码可改进如下:

1 <input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}" />

 (2)这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。

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

2.2 DOM0级事件处理程序

通过JavaScript指定事件处理程序的传统方法,就是将一个函数赋值给一个事件处理程序属性。

每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写,如onclick。将这种属性的值设为一个函数,就可以指定事件处理程序。

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

注意:在这些代码运行前不会指定事件处理程序,因为如果这些代码在页面中位于按钮后面,就有可能在一段时间内怎么点击都没有反应。

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

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

也可以删除通过DOM0级方法指定的事件处理程序:

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

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

2.3 DOM2级事件处理程序

DOM2级事件定义了两个方法。用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,并且他么都接受3个参数。要处理的事件名、作为事件处理程序的函数和一个布尔值。布尔值中true表示在捕获阶段调用事件处理程序;false表示在冒泡阶段调用事件处理程序。

用DOM2级方法添加事件处理程序的好处是可以添加多个事件处理程序:

1 var btn=document.getElementById("myBtn");
2         btn.addEventListener("click",function(){
3             alert(this.id);
4         },false);
5         btn.addEventListener("click",function(){
6             alert("Hello world!");
7         },false);

通过addEventListener()添加的事件处理程序只能通过removeEventListener()方法移除。移除是传入的参数与添加时传入的参数要相同。这也意味中通过addEventListener()添加的匿名函数无法移除。

1 var btn=document.getElementById("myBtn");
2         btn.addEventListener("click",function(){
3             alert(this.id);
4         },false);
5         btn.removeEventListener("click",function(){
6             alert(this.id);
7         },false);//没用用!看似传入了相同的参数,其实这里的第二个参数与addEventListener()中的第二个参数是完全不同的函数

因此应该像下面这种写法:

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


IE9,Opera,Firefox,Chorme和Safari支持DOM2级事件处理程序。

2.4 IE事件处理程序

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

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

在IE中使用attachEvent()与使用DOM0级方法的主要区别是事件处理陈旭的作用域。DOM0中是在元素的作用域内运行;在attachEvent()中是在全局作用域内运行,因此this=window。

1 var btn=document.getElementById("myBtn");
2         btn.attachEvent("onclick",function(){
3              alert(this===window);  //true
4          });

与addEventListener()类似,attachEvent()也可以用来为一个元素添加多个事件处理程序。不同的是attachEvent()不是以添加事件的顺序执行,而是以相反的顺序执行。同样的用attachEvent()添加的时间如果是匿名函数则不能移除,否则可以用detachEvent()方法移除。

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

使用能力检测来隔离浏览器的差异。要保证处理事件的代码能在大多数浏览器下一致的运行,只需关注冒泡阶段。

 1 var EventUtil={
 2         addHandler:function(element,type,handler){
 3             if(element.addEventListener){
 4                 element.addEventListener(type,handler,false);
 5             } else if(element.attachEvent){
 6                 element.attachEvent("on"+type,handler);
 7             } else{
 8                 element["on"+type]=handler;
 9             }
10         },
11         removeHandler:function(element,type,handler){
12             if(element.removeEventListener){
13                 element.removeEventListener(type,handler,false);
14             } else if(element.detchaEvent){
15                 element.detchaEvent("on"+type,handler);
16             } else{
17                 element["on"+type]=null;
18             }
19         }
20     };

可以用像如下代码这样使用以上两个方法:

1  var btn=document.getElementById("myBtn");
2     var handler=function(){
3         alert("Clicked");
4     };
5     EventUtil.addHandler(btn,click,handler);
6     //其他代码
7     EventUtil.removeHandler(btn,click,handler);

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

  

3. 事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

3.1 DOM中的事件对象

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

属性/方法 类型 读/写 说明
bubbles Boolean 只读 表明事件是否冒泡
cancelable Boolean 只读 表明是否可以取消事件的默认行为
currentTarget Element 只读 其事件处理程序当前正在处理时间的那个元素
defaultPrevented Boolean 只读 为true表示已经调用了preventDefault()(DOM3级事件新增)
detail Integer 只读 与事件相关的细节信息
eventPhase Integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示"处于目标",3.表示冒泡阶段
preventDefault() Function 只读 取消事件的默认行为。如果cancelable是true,则可以使用这个方法
stopImmediatePropagation() Function 只读 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级事件新增)
stopPropagation() Function 只读 取消事件的进一步捕获或冒泡,同如果bubbles为true,则可以使用这个方法。
target Element 只读 事件的目标
trusted Boolean 只读 为true表示事件是浏览器生成的。为false表示事件是由开发人员通过JavaScript创建的(DOM3级事件中新增)
type String 只读 被触发的事件的类型
view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。

1  document.body.onclick=function(event){//表示点击按钮时冒泡到Body中后的效果
2         alert(event.currentTarget===document.body);  //true
3         alert(this===document.body);         //true
4         alert(event.target===document.getElementById("myBtn"));    //true
5     }

 当单机到这个例子中的按钮时,this和currentTarget都等于document.body,因为事件处理程序是注册到这个元素上的。然而,target元素却等于按钮元素,因为它是click事件真正的目标。

在需要通过一个函数处理多个事件时,可以使用type属性。如下:

 1  var btn=document.getElementById("myBtn");
 2     var handler=function(event){
 3         switch(event.type){
 4             case "click":
 5             alert("Clicked");
 6             break;
 7 
 8             case "mouseover":
 9             event.target.style.backgroundColor="red";
10             break;
11 
12             case "mouseout":
13             event.target.style.backgroundColor="";
14             break;
15         }
16     };
17 
18     btn.onclick=handler;
19     btn.onmouseover=handler;
20     btn.onmouseout=handler;

要阻止特定事件的默认行为,可以使用preventDefault()方法。例如,链接的默认行为就是在被单击时会导航到其href特性指定的URL。如果你想阻止链接导航这一默认行为,那么通过链接的onclick事件处理程序可以取消它

1 var link=document.getElementById("myLink");
2     link.onclick=function(event){
3         event.preventDefault();
4     }

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

stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。

var btn=document.getElementById("myBtn");
    btn.onclick=function(event){
    	alert("Clicked");
    	event.stopPropagation();
    };
    document.body.onclick=function(event){
    	alert("Body clicked");
    };

  对于这个例子而言,因为调用了stopPropagation(),所以在单价按钮时click事件不会传播到document.body。

事件对象的eventPhase属性,可以用来确定事件当前正位于事件流的哪个阶段。

var btn=document.getElementById("myBtn");
    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
    };

当单击按钮时,首先执行的事件处理程序是在捕获阶段触发的添加到document.body中的那一个,所以会先弹出表示eventPhase的1 。接着会触发按钮上注册的事件处理程序,此时的eventPhase的值为2。最后一个被触发的事件处理程序,是在冒泡阶段执行的添加到document.body上的那一个,显示的值是3。当eventPhase的值为2时,this、target、和currentTarget始终是相等的。

3.2 IE中的事件对象

 在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。 

var btn=document.getElementById("myBtn");
    btn.onclick=function(){
        var event=widnow.event;
        alert(event.type);  //"click"
    };

IE事件对象会包含下表所列的属性和方法

属性/方法 类型 读/写 说明
cancelBubble Boolean 读/写 默认值为false,但将其设置为true就可以取消事件冒泡(与DOM中的stopPropagation()方法的作用相同)
returnValue Boolean 读/写 默认值为true,但假期设置为false就可以取消事件的默认行为(与DOM中的preventDefault()方法的作用相同)
srcElement Element 只读 事件的目标(与DOM中的target属性相同)
type String 只读 被触发的事件的类型

因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标,最好使用event.srcElement比较保险 。

1 var btn=document.getElementById("myBtn");
2     btn.onclick=function(){
3         alert(window.event.srcElement===this);   //true
4     };
5     btn.attachEvent("onclick",function(event){
6       alert(event.srcElement===this);     //false
7     });

3.3 跨浏览器的事件对象

 1  var EventUtil={
 2 
 3         addHandler:function(element,type,handler){
 4             //省略,上面代码有
 5         },
 6 
 7         getEvent:function(event){
 8             return event?event:window.event;
 9         },
10         getTarget:function(event){
11             return event.target||event.srcElement;
12         },
13         preventDefault:function(event){
14             if(event.preventDefault){
15                 event.preventDefault();
16             }else{
17                 event.returnValue=false;
18             }
19         },
20 
21         removeHandler:function(element,type,handler){
22             //省略
23         },
24 
25         stopPropagation:function(event){
26             if(event.stopPropagation){
27                 event.stopPropagation();
28             }else{
29                 event.cancelBubble=true;
30             }
31         }
32     };
原文地址:https://www.cnblogs.com/luoyanan/p/5470213.html