事件冒泡刨晴、事件捕獲、事件委托

一路翻、DOM事件流

  • DOM事件流(event flow)存在三個(gè)階段:事件捕獲階段狈癞、處于目標(biāo)階段、事件冒泡階段茂契,事件冒泡和事件捕獲都是描述事件觸發(fā)時(shí)序問題的術(shù)語蝶桶;
  • 事件捕獲(event capturing):當(dāng)鼠標(biāo)點(diǎn)擊或觸發(fā)dom事件時(shí),瀏覽器會從根節(jié)點(diǎn)開始由外到內(nèi)進(jìn)行事件傳播掉冶,即點(diǎn)擊了子元素真竖,如果父元素通過事件捕獲方式注冊了對應(yīng)事件的話,會先觸發(fā)父元素綁定的事件厌小,是自上而下的恢共;
    事件冒泡(dubbed bubbling):與事件捕獲恰恰相反,事件冒泡順序是由內(nèi)到外進(jìn)行傳播璧亚,直到根節(jié)點(diǎn)讨韭,是自下而上的;
  • 無論是事件捕獲還是事件冒泡涨岁,它們都有一個(gè)共同的行為拐袜,就是事件傳播,它就像一根引線梢薪,只有通過引線才能將綁在引線上的鞭炮(事件監(jiān)聽器)引爆蹬铺;
    DOM事件流

    三個(gè)階段:1-5捕獲過程、5-6目標(biāo)過程秉撇、6-10冒泡過程
    二甜攀、事件綁定方法:addEventListener、attachEvent
  • addEventListener
document.getElementById("child").addEventListener("click", function(e) {
      console.log("child事件被觸發(fā)琐馆," + this.id)
 },true)
  • attachEvent
element.attachEvent('onclick', function(){
        // ...
});

三规阀、事件冒泡

  • 綁定事件方法的第三個(gè)參數(shù),就是控制事件觸發(fā)順序是否為事件捕獲瘦麸,true為事件捕獲谁撼,false為事件冒泡,默認(rèn)為false即事件冒泡滋饲,JQuery的e.stopPropagation會阻止冒泡厉碟,意思是到我為止喊巍,我的上一級的事件就不要觸發(fā)了;
// 事件冒泡
<body>
  <div id="parent">
    <div id="child" class="child">child</div>
  </div>
  <script type="text/javascript">
    document.getElementById("parent").addEventListener("click", function(e) {
      console.log("parent事件被觸發(fā)箍鼓," + this.id);
    })
    document.getElementById("child").addEventListener("click", function(e) {
      console.log("child事件被觸發(fā)崭参," + this.id)
    })
    // child事件被觸發(fā),child
    // parent事件被觸發(fā)款咖,parent
  </script>
</body>

二何暮、事件捕獲

// 事件捕獲
<body>
  <div id="parent">
    <div id="child" class="child">child</div>
  </div>
  <script type="text/javascript">
    document.getElementById("parent").addEventListener("click", function(e) {
      console.log("parent事件被觸發(fā)," + this.id);
    },true)
    document.getElementById("child").addEventListener("click", function(e) {
      console.log("child事件被觸發(fā)铐殃," + this.id)
    },true)
    // parent事件被觸發(fā)海洼,parent
    // child事件被觸發(fā),child
  </script>
</body>
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <style>
    #parent {
      width: 200px;
      height: 200px;
      text-align: center;
      line-height: 3;
      background: green;
    }

    #child {
      width: 100px;
      height: 100px;
      margin: 0 auto;
      background: orange;
    }
  </style>
</head>

<body>
  <div id="parent">
    父元素
    <div id="child">
      子元素
    </div>
  </div>
  <script type="text/javascript">
    var parent = document.getElementById("parent");
    var child = document.getElementById("child");

    document.body.addEventListener("click", function(e) {
      console.log("click-body");
    }, false);

    parent.addEventListener("click", function(e) {
      console.log("click-parent");
    }, false);

    //新增事件捕獲事件代碼
    parent.addEventListener("click", function(e) {
      console.log("click-parent--事件捕獲");
    }, true);

    child.addEventListener("click", function(e) {
      console.log("click-child");
      // e.stopPropagation(); // 停止事件傳播
    }, false);
  </script>
</body>
</html>
事件冒泡和事件捕獲
<style type="text/css">
    #wrapDiv, #innerP, #textSpan{
        margin: 5px;padding: 5px;box-sizing: border-box;cursor: default;
    }
    #wrapDiv{
        width: 300px;height: 300px;border: indianred 3px solid;
    }
    #innerP{
        width: 200px;height: 200px;border: hotpink 3px solid;
    }
    #textSpan{
        display: block;width: 100px;height: 100px;border: orange 3px solid;
    }
    </style>
</head>
<body>
     <div id="wrapDiv">wrapDiv
        <p id="innerP">innerP
            <span id="textSpan">textSpan</span>
        </p>
    </div>
    <script>
    var wrapDiv = document.getElementById("wrapDiv");
    var innerP = document.getElementById("innerP");
    var textSpan = document.getElementById("textSpan");

    // 捕獲階段綁定事件
    window.addEventListener("click", function(e){
        console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    document.addEventListener("click", function(e){
        console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    document.documentElement.addEventListener("click", function(e){
        console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    document.body.addEventListener("click", function(e){
        console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    wrapDiv.addEventListener("click", function(e){
        console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    innerP.addEventListener("click", function(e){
        console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    textSpan.addEventListener("click", function(e){
        console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName);
    }, true);

    // 冒泡階段綁定的事件
    window.addEventListener("click", function(e){
        console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    document.addEventListener("click", function(e){
        console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    document.documentElement.addEventListener("click", function(e){
        console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    document.body.addEventListener("click", function(e){
        console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    wrapDiv.addEventListener("click", function(e){
        console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    innerP.addEventListener("click", function(e){
        console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);

    textSpan.addEventListener("click", function(e){
        console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName);
    }, false);
</script>
</body>
  • target和currentTarget都是event上面的屬性背稼,target是真正發(fā)生事件的DOM元素贰军,而currentTarget是當(dāng)前事件發(fā)生在哪個(gè)DOM元素上;目標(biāo)階段也就是 target == currentTarget的時(shí)候蟹肘,由于window沒有nodeName這個(gè)屬性词疼,所以是undefined;
  • 在目標(biāo)元素上不會遵守先發(fā)生捕獲后發(fā)生冒泡這一規(guī)則帘腹,而是先綁定的事件先發(fā)生贰盗;


    事件冒泡和事件捕獲案例2

五、事件委托

var parent = document.getElementById("parent");
    var child = document.getElementById("child");
    parent.onclick = function(e) {
      if (e.target.id == "child") {
        console.log("您點(diǎn)擊了child元素")
      }
      if (e.target.id == "parent") {
        console.log("您點(diǎn)擊了parent元素")
      }
    }

六阳欲、事件綁定的方法

  • 直接獲取元素綁定:
element.onclick = function(e){
        // ...
};

優(yōu)點(diǎn):簡單和穩(wěn)定舵盈,可以確保它在你使用的不同瀏覽器中運(yùn)作一致;處理事件時(shí)球化,this關(guān)鍵字引用的是當(dāng)前元素秽晚,這很有幫組;
缺點(diǎn):只會在事件冒泡中運(yùn)行;一個(gè)元素一次只能綁定一個(gè)事件處理函數(shù)筒愚,新綁定的事件處理函數(shù)會覆蓋舊的事件處理函數(shù)赴蝇;事件對象參數(shù)(e)僅非IE瀏覽器可用;

  • 直接在元素里面使用事件屬性:
  • W3C方法:
element.addEventListener('click', function(e){
        // ...
}, false);

優(yōu)點(diǎn):該方法同時(shí)支持事件處理的捕獲和冒泡階段;事件階段取決于addEventListener最后的參數(shù)設(shè)置:false (冒泡) 或 true (捕獲)巢掺;在事件處理函數(shù)內(nèi)部句伶,this關(guān)鍵字引用當(dāng)前元素;事件對象總是可以通過處理函數(shù)的第一個(gè)參數(shù)(e)捕獲陆淀;可以為同一個(gè)元素綁定你所希望的多個(gè)事件考余,同時(shí)并不會覆蓋先前綁定的事件;
缺點(diǎn):IE不支持,你必須使用IE的attachEvent函數(shù)替代;

  • IE下的方法:
element.attachEvent('onclick', function(){
        // ...
});

優(yōu)點(diǎn):可以為同一個(gè)元素綁定你所希望的多個(gè)事件轧苫,同時(shí)并不會覆蓋先前綁定的事件;
缺點(diǎn):IE僅支持事件捕獲的冒泡階段楚堤;事件監(jiān)聽函數(shù)內(nèi)的this關(guān)鍵字指向了window對象,而不是當(dāng)前元素(IE的一個(gè)巨大缺點(diǎn));事件對象僅存在與window.event參數(shù)中身冬;事件必須以ontype的形式命名鳄袍,比如,onclick而非click吏恭;僅IE可用,你必須在非IE瀏覽器中使用W3C的addEventListener重罪;
注意:不是意味這低版本的ie沒有事件捕獲樱哼,它也是先發(fā)生事件捕獲,再發(fā)生事件冒泡剿配,只不過這個(gè)過程無法通過程序控制搅幅;
七、解除事件

element.removeEventListener('click', function(e){
        // ...
}, false);

IE:

element.detachEvent('onclick', function(){
        // ...
});

八呼胚、阻止事件
在支持addEventListener()的瀏覽器中茄唐,可以調(diào)用事件對象的stopPropagation()方法以阻止事件的繼續(xù)傳播,如果在同一對象上定義了其他處理程序蝇更,剩下的處理程序?qū)⒁琅f被調(diào)用沪编,但調(diào)用stopPropagation()之后任何其他對象上的事件處理程序?qū)⒉粫徽{(diào)用,不僅可以阻止事件在冒泡階段的傳播年扩,還能阻止事件在捕獲階段的傳播蚁廓;
IE9之前的IE不支持stopPropagation()方法,而是設(shè)置事件對象cancelBubble屬性為true來實(shí)現(xiàn)阻止事件進(jìn)一步傳播厨幻;
九相嵌、阻止事件的默認(rèn)行為
e.preventDefault()可以阻止事件的默認(rèn)行為發(fā)生,默認(rèn)行為是指:點(diǎn)擊a標(biāo)簽就轉(zhuǎn)跳到其他頁面况脆、拖拽一個(gè)圖片到瀏覽器會自動(dòng)打開饭宾、點(diǎn)擊表單的提交按鈕會提交表單等等,因?yàn)橛械臅r(shí)候我們并不希望發(fā)生這些事情格了,所以需要阻止默認(rèn)行為看铆;
IE9之前的IE中,可以通過設(shè)置事件對象的returnValue屬性為false達(dá)到同樣的效果笆搓;

function cancelHandler(event){
    var event=event||window.event;//兼容IE
    
    //取消事件相關(guān)的默認(rèn)行為
    if(event.preventDefault)    //標(biāo)準(zhǔn)技術(shù)
        event.preventDefault();
    if(event.returnValue)    //兼容IE9之前的IE
        event.returnValue=false;
    return false;    //用于處理使用對象屬性注冊的處理程序
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末性湿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子满败,更是在濱河造成了極大的恐慌肤频,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件算墨,死亡現(xiàn)場離奇詭異宵荒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門报咳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侠讯,“玉大人,你說我怎么就攤上這事暑刃∠徜觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵岩臣,是天一觀的道長溜嗜。 經(jīng)常有香客問我,道長架谎,這世上最難降的妖魔是什么炸宵? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮谷扣,結(jié)果婚禮上土全,老公的妹妹穿的比我還像新娘。我一直安慰自己会涎,他們只是感情好裹匙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著在塔,像睡著了一般幻件。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛔溃,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天绰沥,我揣著相機(jī)與錄音,去河邊找鬼贺待。 笑死徽曲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的麸塞。 我是一名探鬼主播秃臣,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哪工!你這毒婦竟也來了奥此?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤雁比,失蹤者是張志新(化名)和其女友劉穎稚虎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偎捎,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蠢终,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年序攘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寻拂。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡程奠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祭钉,到底是詐尸還是另有隱情瞄沙,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布慌核,位于F島的核電站帕识,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏遂铡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一晶姊、第九天 我趴在偏房一處隱蔽的房頂上張望扒接。 院中可真熱鬧,春花似錦们衙、人聲如沸钾怔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宗侦。三九已至,卻和暖如春忆蚀,著一層夾襖步出監(jiān)牢的瞬間矾利,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工馋袜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留男旗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓欣鳖,卻偏偏與公主長得像察皇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子泽台,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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