關(guān)于事件冒泡匹舞、事件捕獲和事件委托

在javascript里,事件委托是很重要的一個東西似芝,事件委托依靠的就是事件冒泡和捕獲的機制那婉,我先來解釋一下事件冒泡和事件捕獲:

事件冒泡會從當前觸發(fā)的事件目標一級一級往上傳遞,依次觸發(fā)党瓮,直到document為止详炬。
事件捕獲會從document開始觸發(fā),一級一級往下傳遞寞奸,依次觸發(fā)呛谜,直到真正事件目標為止。

這么說是不是很抽象枪萄,其實就像我敲擊了一下鍵盤呻率,我在敲擊鍵盤的同時,我是不是也敲擊了這臺電腦呻引,我寫個例子大家就明白了:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <style type="text/css">
        #box1 { width: 300px; height: 300px; background: blueviolet; }
        #box2 { width: 200px; height: 200px; background: aquamarine; }
        #box3 { width: 100px; height: 100px; background: tomato; }
        div { overflow: hidden; margin: 50px auto; }
    </style>
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3"></div>
            </div>
        </div>
        <script>
            function sayBox3() {
                console.log('你點了最里面的box');
            }
            function sayBox2() {
                console.log('你點了最中間的box');
            }
            function sayBox1() {
                console.log('你點了最外面的box');
            }
            // 事件監(jiān)聽礼仗,第三個參數(shù)是布爾值,默認false逻悠,false是事件冒泡元践,true是事件捕獲
            document.getElementById('box3').addEventListener('click', sayBox3, false);
            document.getElementById('box2').addEventListener('click', sayBox2, false);
            document.getElementById('box1').addEventListener('click', sayBox1, false);

        </script>
    </body>
</html>

我們畫了三個box,結(jié)構(gòu)是父子關(guān)系童谒,分別綁定了打印事件单旁,現(xiàn)在我們來點擊最中間的紅色box:



我們發(fā)現(xiàn),我們僅僅是點擊了紅色的box饥伊,但是綠色和紫色的box也被觸發(fā)了打印事件象浑,觸犯順序是 紅色>綠色>紫色,這種現(xiàn)象就是事件冒泡了琅豆。
我們再試試事件捕獲愉豺,把上面代碼里監(jiān)聽事件的第三個參數(shù)改為true,然后點擊紅色的box:



我們還是只點擊最中間的紅色box茫因,和上一次一樣蚪拦,也是三個box都觸發(fā)了事件,但是順序反過來了,紫色>綠色>紅色驰贷,這種現(xiàn)象稱為事件捕獲盛嘿。
通過上面的例子,應(yīng)該很容易就理解了事件冒泡和事件捕獲括袒,我們平時都是默認冒泡的次兆,冒泡是一直冒到document根文檔為止。

現(xiàn)在來談?wù)勈录星旅蹋录杏址Q之為事件代理芥炭,我們通過一個通俗的例子來解釋:

有三個同事預(yù)計會在周一收到快遞,為了簽收快遞城须,有兩種辦法:1.三個人在公司門口等快遞蚤认;2.委托給前臺MM代為簽收。現(xiàn)實當中糕伐,我們大都采用委托的方案(公司也不會容忍那么多員工站在門口就為了等快遞)砰琢。前臺MM收到快遞后,她會判斷收件人是誰良瞧,然后按照收件人的要求簽收陪汽,甚至代為付款。這種方案還有一個優(yōu)勢褥蚯,那就是即使公司里來了新員工(不管多少)挚冤,前臺MM也會在收到寄給新員工的快遞后核實并代為簽收(可以給暫時不存在的節(jié)點也綁定上事件)。

我們再舉另一個例子:

現(xiàn)在有一個ul赞庶,ul里又有100個li训挡,我想給這100個li都綁定一個點擊事件,我們一般可以通過for循環(huán)來綁定歧强,但是要是有1000個li呢澜薄? 為了提高效率和速度,所以我們這時可以采用事件委托摊册,只給ul綁定一個事件肤京,根據(jù)事件冒泡的規(guī)則,只要你點了ul里的每一個li茅特,都會觸發(fā)ul的綁定事件忘分,我們在ul綁定事件的函數(shù)里通過一些判斷,就可以給這100li都觸發(fā)點擊事件了白修。

具體怎么實現(xiàn)妒峦,看代碼:

// 這里不講IE,結(jié)尾再說
function clickLi() {
    alert('你點擊了li');
}
document.getElementById('isUl').addEventListener('click', function(event) {
    // 每一個函數(shù)內(nèi)都有一個event事件對象熬荆,它有一個target屬性舟山,指向事件源
    var src = event.target;
    // 我們判斷如果target事件源的節(jié)點名字是li绸狐,那就執(zhí)行這個函數(shù)
    // target里面的屬性是非常多的卤恳,id名累盗、class名、節(jié)點名等等都可以取到
    if(src.nodeName.toLowerCase() == 'li') {
       clickLi() 突琳;
    }
});

這樣我們就通過給ul綁定一個點擊事件若债,讓所有的li都觸發(fā)了函數(shù)。
那如果想給不同的li綁定不同的函數(shù)怎么辦拆融?
假設(shè)有3個li蠢琳,我們先寫3個不同的函數(shù),再給3個li設(shè)置不同的id名镜豹,通過判斷id名是不是就能給不同的li綁定不同的函數(shù)啦:

<body>
    <ul id="isUl">
        <li id="li01">1</li>
        <li id="li02">2</li>
        <li id="li03">3</li>
    </ul>
    <script>
        function clickLi01() {
            alert('你點擊了第1個li');
        }
        function clickLi02() {
            alert('你點擊了第2個li');
        }
        function clickLi03() {
            alert('你點擊了第3個li');
        }
        document.getElementById('isUl').addEventListener('click', function(event) {
            var srcID = event.target.id;
            if(srcID == 'li01'){
                clickLi01();
            }else if(srcID == 'li02') {
                clickLi02();
            }else if(srcID == 'li03') {
                clickLi03();
            }
        });
    </script>
</body>

這就是所謂的事件委托傲须,通過監(jiān)聽一個父元素,來給不同的子元素綁定事件趟脂,減少監(jiān)聽次數(shù)泰讽,從而提升速度。
那么昔期,能不能阻止元素的事件冒泡呢已卸,答案是可以的。
一開始那個例子硼一,假如我們真的只想點擊最里面的那個紅色box累澡,不想另外兩個box的事件被觸發(fā),我們可以在給紅色box綁定事件的函數(shù)里這么寫:

function sayBox3(event) {
    // 阻止冒泡
    event.stopPropagation();
    console.log('你點了最里面的box');
}
document.getElementById('box3').addEventListener('click', sayBox3, false);

這樣我們再點擊紅色的box般贼,那就只會觸發(fā)它本身的事件啦愧哟。

那阻止冒泡有沒有實際用途呢?答案是有的哼蛆,我們看這個例子:


這是一個模態(tài)框蕊梧,現(xiàn)在的需求是當我們點擊紅色的按鈕需要跳轉(zhuǎn)頁面,然后點擊白色的對話框不需要任何反應(yīng)人芽,點其它任何地方就關(guān)閉這個模態(tài)框望几。
這里就需要用到阻止冒泡了,紅色的按鈕是白色對話框的子元素萤厅,白色對話框又是這整個模態(tài)框的子元素橄抹,我們給模態(tài)框加上一個點擊事件關(guān)閉,然后給紅色的按鈕加上一個點擊事件跳轉(zhuǎn)惕味,這時就產(chǎn)生了一個問題楼誓,只要點擊白色的對話框,由于冒泡機制名挥,這個模態(tài)框也會關(guān)閉疟羹,實際上我們并不想點擊白色的對話框有任何反應(yīng),這時我們就給這個白色的對話框綁定一個點擊事件,函數(shù)里寫上event.stopPropagation();榄融,這樣就OK了参淫。

關(guān)于IE

老版本的IE存在兼容問題,根本不支持addEventListener()的添加事件和removeEventListener()的刪除事件愧杯,它有自己的監(jiān)聽方法:

// 添加事件涎才,事件流固定為冒泡
attachEvent(事件名,事件處理函數(shù))
// 刪除事件
detachEvent(事件名力九,事件處理函數(shù))

還有IE里的事件對象是window.event耍铜,事件源是srcElement,阻止冒泡寫法也不一樣:

function() {
    // IE里阻止冒泡
    window.event.cancelBubble = true;
    // IE里獲取事件源的id
    var srcID = window.event.srcElement.id;
}
function(event) {
    // 非IE里阻止冒泡
    event.stopPropagation();
    // 非IE里獲取事件源的id
    var srcID = event.target.id;
}

補充

關(guān)于js的瀏覽器兼容問題跌前,一般用能力檢測來解決棕兼,if(){}else{}
我們平時工作一般都是用jquery,IE這些特殊情況早就幫我們做好兼容啦抵乓。
jquery在1.7的版本之后伴挚,最流行的事件監(jiān)聽方法是$(元素).on(事件名,執(zhí)行函數(shù))臂寝,它還有一種事件委托的寫法$(委托給哪個元素).on(事件名章鲤,被委托的元素,執(zhí)行函數(shù))


最后說一點咆贬,如果元素被阻止冒泡了败徊,千萬別去用事件委托的方式監(jiān)聽事件,因為事件委托的原理是利用事件冒泡掏缎,當冒泡被阻止皱蹦,就無法監(jiān)聽了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末眷蜈,一起剝皮案震驚了整個濱河市沪哺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酌儒,老刑警劉巖辜妓,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異忌怎,居然都是意外死亡籍滴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門榴啸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孽惰,“玉大人,你說我怎么就攤上這事鸥印⊙Γ” “怎么了坦报?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狂鞋。 經(jīng)常有香客問我片择,道長,這世上最難降的妖魔是什么要销? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任构回,我火速辦了婚禮夏块,結(jié)果婚禮上疏咐,老公的妹妹穿的比我還像新娘。我一直安慰自己脐供,他們只是感情好浑塞,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著政己,像睡著了一般酌壕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上歇由,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天卵牍,我揣著相機與錄音,去河邊找鬼沦泌。 笑死糊昙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谢谦。 我是一名探鬼主播释牺,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼回挽!你這毒婦竟也來了没咙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤千劈,失蹤者是張志新(化名)和其女友劉穎祭刚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墙牌,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡涡驮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了憔古。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遮怜。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸿市,靈堂內(nèi)的尸體忽然破棺而出锯梁,到底是詐尸還是另有隱情即碗,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布陌凳,位于F島的核電站剥懒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏合敦。R本人自食惡果不足惜初橘,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望充岛。 院中可真熱鬧保檐,春花似錦、人聲如沸崔梗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒜魄。三九已至扔亥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谈为,已是汗流浹背旅挤。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伞鲫,地道東北人粘茄。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像榔昔,于是被迫代替她去往敵國和親驹闰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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