JavaScript事件——事件冒泡骗村,事件捕獲,事件綁定與解綁呀枢,事件委托

一.事件

事件是用戶或瀏覽器自身執(zhí)行的某種動作胚股,這是我自己的理解。


二.事件流

事件流描述的是從頁面中接收事件的順序,也可理解為事件在頁面中傳播的順序裙秋。

1.兩種事件流模型

冒泡型事件流:事件的傳播從DOM樹的葉子到根琅拌。【推薦】——event bubbling
捕獲型事件流:事件的傳播蔥DOM樹的根到葉子摘刑。 ——event capturing

兩種事件流模型.png
2.W3C標(biāo)準(zhǔn)模型

W3C標(biāo)準(zhǔn)采用捕獲+冒泡进宝。兩種事件流都會觸發(fā)DOM的所有對象,從document對象開始枷恕,也在document對象結(jié)束党晋。

W3C標(biāo)準(zhǔn)模型.png

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事件暫且先寫這么多榕茧,歡迎大家指正博文中錯誤。謝謝觀看客给!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末用押,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子靶剑,更是在濱河造成了極大的恐慌蜻拨,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抬虽,死亡現(xiàn)場離奇詭異官觅,居然都是意外死亡,警方通過查閱死者的電腦和手機阐污,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門休涤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笛辟,你說我怎么就攤上這事功氨。” “怎么了手幢?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵捷凄,是天一觀的道長。 經(jīng)常有香客問我围来,道長跺涤,這世上最難降的妖魔是什么匈睁? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮桶错,結(jié)果婚禮上航唆,老公的妹妹穿的比我還像新娘。我一直安慰自己院刁,他們只是感情好糯钙,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著退腥,像睡著了一般任岸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狡刘,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天享潜,我揣著相機與錄音,去河邊找鬼颓帝。 笑死米碰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的购城。 我是一名探鬼主播吕座,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘪板!你這毒婦竟也來了吴趴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤侮攀,失蹤者是張志新(化名)和其女友劉穎锣枝,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兰英,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡撇叁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了畦贸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陨闹。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖薄坏,靈堂內(nèi)的尸體忽然破棺而出趋厉,到底是詐尸還是另有隱情,我是刑警寧澤胶坠,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布君账,位于F島的核電站,受9級特大地震影響沈善,放射性物質(zhì)發(fā)生泄漏乡数。R本人自食惡果不足惜椭蹄,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望净赴。 院中可真熱鬧塑娇,春花似錦、人聲如沸劫侧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烧栋。三九已至,卻和暖如春拳球,著一層夾襖步出監(jiān)牢的瞬間审姓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工祝峻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留魔吐,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓莱找,卻偏偏與公主長得像酬姆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奥溺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容