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