一.事件
事件是用戶或瀏覽器自身執(zhí)行的某種動作胚股,這是我自己的理解。
二.事件流
事件流描述的是從頁面中接收事件的順序,也可理解為事件在頁面中傳播的順序裙秋。
1.兩種事件流模型
冒泡型事件流:事件的傳播從DOM樹的葉子到根琅拌。【推薦】——event bubbling
捕獲型事件流:事件的傳播蔥DOM樹的根到葉子摘刑。 ——event capturing
2.W3C標(biāo)準(zhǔn)模型
W3C標(biāo)準(zhǔn)采用捕獲+冒泡进宝。兩種事件流都會觸發(fā)DOM的所有對象,從document對象開始枷恕,也在document對象結(jié)束党晋。
W3C標(biāo)準(zhǔn)則取其折中方案. W3C事件模型中發(fā)生的任何事件, 先(從其祖先元素window)開始一路向下捕獲, 直到達到目標(biāo)元素, 其后再次從目標(biāo)元素開始冒泡.
而你作為開發(fā)者, 可以決定事件處理器是注冊在捕獲或者是冒泡階段. 如果addEventListener的最后一個參數(shù)是true, 那么處理函數(shù)將在捕獲階段被觸發(fā); 否則(false), 會在冒泡階段被觸發(fā).
3.瀏覽器兼容問題
一些標(biāo)準(zhǔn)的瀏覽器中,如Chrome徐块、Firefox等未玻,支持這兩種方式。而事實上胡控,準(zhǔn)確的說扳剿,是這兩種方式的混合方式。
在低版本IE及Opera下昼激,是不支持事件捕獲的庇绽。而這個特點最明顯體現(xiàn)在事件綁定函數(shù)上。
IE橙困、Opera的事件綁定函數(shù)是attachEvent瞧掺,而Chrome等瀏覽器則是addEventListener,而后者比前者的參數(shù)多了一個凡傅,這個參數(shù)是一個布爾值夸盟。這個布爾值由用戶決定,用戶若設(shè)為true像捶,則該綁定事件以事件捕獲的形式參與,若為false則以事件冒泡的形勢參與桩砰。
三.事件綁定與解綁
1.直接在HTML中事件處理(不推薦)
<input type="button" value="showClick" onclick="showClick()" />
這種方法的有很多缺點:
(1)首先拓春,存在一個時差問題。因為用戶如果會在 HTML 元素一出現(xiàn)在頁面上就觸發(fā)相應(yīng)的事件亚隅,但當(dāng)時的事件處理程序有可能尚不具備執(zhí)行條件硼莽。假如用戶在頁面解析事件處理函數(shù)之前就觸發(fā)事件,就會引發(fā)錯誤。為此懂鸵,很多HTML事件處理程序都會被封裝在一個 try-catch 塊中偏螺,這樣錯誤不會浮出水面,因為在瀏覽器有機會處理錯誤之前匆光,錯誤就被捕獲了套像。如下面的例子所示:
<input type="button" value="showClick" onclick="try{showClick();}catch(ex){}">
(2)這樣擴展事件處理程序的作用域鏈在不同瀏覽器中會導(dǎo)致不同結(jié)果。不同 JavaScript 引擎遵循的標(biāo)識符解析規(guī)則略有差異终息,很可能會在訪問非限定對象成員時出錯夺巩。
(3)HTML 與 JavaScript 代碼緊密耦合。如果要更換事件處理程序周崭,就要改動兩個地方:HTML 代碼和 JavaScript 代碼柳譬。而這正是許多開發(fā)人員摒棄 HTML 事件處理程序,轉(zhuǎn)而使用 JavaScript 指定事件處理程序的原因所在续镇。
2. 直接在dom對象上注冊事件名稱美澳,就是DOM0寫法,所有瀏覽器支持
//事件綁定
document.getElementById("patty").onclick = function(e){};/
document.getElementById("patty")["onmousemover"] = function(e){};
//事件解綁
document.getElementById("patty")["onmousemover"] = null;
//阻止默認事件(默認事件行為:href=""鏈接摸航,submit表單提交等)
document.getElementById("patty").onclick = function() {
…… //你的代碼
return false; //通過返回false值阻止默認事件行為
};
在這里我覺得有必要解釋兩點
(1)事件綁定通過.和[]的兩種方式訪問js對象屬性的方法制跟,[]的形式主要是為了解決屬性名不是合法的標(biāo)識符,比如:object.123肯定報錯忙厌,但是object["123"]就避免了這個問題凫岖,與此同時,[]的寫法逢净,更加靈活哥放,用字符串表示屬性名稱,可以在運行時動態(tài)綁定事件爹土。
(2)return false 的含義不是阻止事件繼續(xù)向頂層元素傳播甥雕,而是阻止瀏覽器對事件的默認處理。 return false 只在當(dāng)前函數(shù)有效胀茵,不會影響其他外部函數(shù)的執(zhí)行社露。
總結(jié)
retrun true; 返回正確的處理結(jié)果琼娘。
return false峭弟;返回錯誤的處理結(jié)果,終止處理脱拼;阻止提交表單瞒瘸;阻止執(zhí)行默認的行為。
return熄浓;把控制權(quán)返回給頁面情臭。
3.DOM2事件模型
· DOM2支持同一dom元素注冊多個同種事件。
· DOM2新增了捕獲和冒泡的概念。
(1)addEventListener(event.type, handle, boolean); IE8及以下不支持
事件類型沒有on俯在,第三個參數(shù)false 表示在事件第三階段(冒泡)觸發(fā)竟秫,true表示在事件第一階段(捕獲)觸發(fā)。 如果handle是同一個方法跷乐,只執(zhí)行一次肥败。
var element=document.getElementById("patty");
var handler=function(){ }
//綁定事件
element.addEventListener('click', handler, false);
//解綁事件
element.removeEventListener('click', handle, false);
//阻止默認事件
element.addEventListener("click", function(e){
var event = e || window.event;
……
event.preventDefault( ); //阻止默認事件
},false);
(2)attachEvent(event.type, handle ); IE特有,兼容IE8及以下劈猿,可添加多個事件處理程序拙吉,只支持冒泡階段,并不屬于DOM2
如果handle是同一個方法揪荣,綁定幾次執(zhí)行幾次筷黔,這點和addEventListener不同。事件類型要加on,例如onclick而不是click
特別注意:
在 IE 中使用 attachEvent() 與DOM0和DOM2addEventListener有一主要區(qū)別:事件處理程序的作用域仗颈。在這些方法中佛舱,事件處理程序會在其所屬元素的作用域內(nèi)運行;在使用 attachEvent() 方法的情況下挨决,事件處理程序會在全局作用域中運行请祖,因此 this 等于 window。
var element=document.getElementById("patty");
var handler=function(){ }
/綁定事件
element.attachEvent('onclick', handler);
//解綁事件脖祈,參數(shù)和綁定一樣
element.detachEvent("onclick", handler);
//阻止默認事件
element.attachEvent("onclick", function(e){
var event = e || window.event;
……
event.returnValue = false; //阻止默認事件
},false);
(3)封裝事件綁定與解綁函數(shù),兼容瀏覽器
// 事件綁定
function addEvent(element, eType, handler, bol) {
if(element.addEventListener){ //如果支持addEventListener
element.addEventListener(eType, handler bol);
}else if(element.attachEvent){ //如果支持attachEvent
element.attachEvent("on"+eType, handler);
}else{ //否則使用兼容的onclick綁定
element["on"+eType] = handle;
}
}
// 事件解綁
function removeEvent(element, eType, handler, bol) {
if(element.addEventListener){
element.removeEventListener(eType, handler, bol);
}else if(element.attachEvent){
element.detachEvent("on"+eType, handler);
}else{
element["on"+eType] = null;
}
}
作為一個追求完美的人肆捕,我們可以將兩個函數(shù)寫成函數(shù)的方法模式
//用事件冒泡方式,如果想兼容事件捕獲只需要添加個bool參數(shù)
var EventUtil = {
addHandler: function(element,type,handler) {
if (element.addEventListener) {
element.addEventListener(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.removeEventListener(type,handler,false);
}
else if(element.detachEvent) {
element.detachEvent('on' +type,handler);
}
else {
element['on'+type] = null;
}
}
}
//使用
var patty= document.getElementById("patty");
var handler = function(){
console.log("Don't touch patty");
};
EventUtil.addHandler(patty, "click", handler);
EventUtil.removeHandler(patty, "click", handler);
四.終止事件冒泡
事件冒泡盖高、事件捕獲阻止:
event.stopPropagation( ); // 阻止事件的進一步傳播慎陵,包括(冒泡,捕獲)喻奥,無參數(shù) 席纽,ie不支持
event.cancelBubble = true; // true 為阻止冒泡,也有瀏覽器不支持
方法1:利用event.stopPropagation( )和event.cancelBubble
var patty=document.getElementById("patty").addEventListener("click",function(event){
var event = event||window.event;
……
event.stopPropagation();
});
這里提供一個阻止冒泡兼容性寫法
function stopPropagation(event){
event=window.event||event;
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
//直接調(diào)用
document.getElementById("patty").addEventListener("click",function(event){
......
stopPropagation(event);
}
方法2:利用event.target和event.currentTarget,事件包含最初觸發(fā)事件的節(jié)點引用 和 當(dāng)前處理事件節(jié)點的引用撞蚕,節(jié)點只處理自己觸發(fā)的事件
document.getElementById("patty").addEventListener("click",function(event){
event=window.event||event;
//IE沒有event.target润梯,有event.srcElement
var target = event.target||event.srcElement;
if(target == event.currentTarget)
{
……
}
});
方法一缺點:為了實現(xiàn)點擊特定的元素顯示對應(yīng)的信息,方法一要求每個元素的子元素也必須終止事件的冒泡傳遞甥厦,即跟別的元素功能上強關(guān)聯(lián)纺铭,這樣的方法會很脆弱。
方法二缺點:方法二為每一個元素都增加了事件監(jiān)聽處理函數(shù)刀疙,事件的處理邏輯都很相似舶赔,即都有判斷 if(target == event.currentTarget),這樣存在了很大的代碼冗余庙洼,現(xiàn)在是三個元素還好,當(dāng)有10幾個,上百個又該怎么辦呢油够?
改進(利用事件委托)
讓某個父節(jié)點統(tǒng)一處理事件蚁袭,通過判斷事件的發(fā)生地(即事件產(chǎn)生的節(jié)點),然后做出相應(yīng)的處理
這里我們先了解下事件委托的概念
事件委托:
利用事件冒泡的特性石咬,將里層的事件委托給外層事件揩悄,根據(jù)event對象的屬性進行事件委托,改善性能鬼悠。使用事件委托能夠避免對特定的每個節(jié)點添加事件監(jiān)聽器删性;事件監(jiān)聽器是被添加到它們的父元素上。事件監(jiān)聽器會分析從子元素冒泡上來的事件焕窝,找到是哪個子元素的事件蹬挺。
window.onload = function() {
document.getElementById("box").addEventListener("click",eventPerformed,false);
}
function eventPerformed(event) {
var event = event||window.event;
var target = e.target||e.srcElement;
switch (target.id) {
case "div1":
alert("您好,我是div1它掂。");
break;
case "div2":
alert("您好巴帮,我是div2。");
break;
}
}
五.最后給大家送上一個跨瀏覽器的事件對象
var EventUtil={
getEvent:function(event){
return event||window.event;
},
getTarget:function(event){
return event.target||event.srcElement;
},
preventDefault:function(){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
stopPropagation:function(){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
},
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element["e"+type]=function(){
handler.call(element)
}
element.attachEvent("on"+type,element["e"+type]);
}else{
element["on"+type]=handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,element["e"+type]);
element["e"+type]=null;
}else{
element["on"+type]=null;
}
}
};
//使用
var patty=document.getElementById("patty");
var handler=function(event){
var event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
alert(target.id);
EventUtil.stopPropagation(event);
}
EventUtil.addHandler(patty,'click',handler);
好了虐秋,關(guān)于JS事件暫且先寫這么多榕茧,歡迎大家指正博文中錯誤。謝謝觀看客给!