天天看點

js中的冒泡,捕獲以及事件委托

之前學習冒泡和捕獲時有些囫囵吞棗,加之很長一點時間沒有做過dom類的工作,是以就遺忘了.這次因為項目中有一些dom操作需要我來寫,是以又需要去研究一下.故總結出一些我個人的見解跟大家分享.如有了解不對的地方,歡迎各位不吝賜教,批評佐證.

什麼是冒泡和捕獲

冒泡和捕獲是dom中的兩種事件流,現代浏覽器基本都遵循這兩種事件流方式.

上面這句話可能比較抽象,廢話不多說,那直接上代碼,讓我們通過現象去了解本質

<div id='far'>
        <div id='son'></div>
    </div>
           
//js es6
    let far = document.getElementById('far');
    let son = document.getElementById('son');
    far.addEventListener('click',()=>console.log('我是far'));
    son.addEventListener('click',()=>console.log('我是son'));

    //執行以上代碼,當點選son時,你會發現far的點選事件也被觸發了.
           
js中的冒泡,捕獲以及事件委托

很多人把這種現象叫做事件冒泡,起初我也是這樣認為的.但是當我學習了事件流中的冒泡模型和捕獲模型後,我就改變了想法(我認為,冒泡和捕獲隻是事件流執行的兩種規則,後面會具體說).因為當你在點選son的時候,其實你點選的位置也在far的範圍内,是以far的點選事件被觸發是正常的.

那麼也就可以很好的了解,為什麼當我們不給son注冊點選事件,而我們還是在son的範圍内點選滑鼠時,far依然會觸發.

那麼說到這裡,我們就可以談一談冒泡啦.

此時,我們需要先考慮一個問題:

為什麼先觸發的不是far,而是son呢?

要回答這個問題,首先我們要知道,當事件觸發時,事件會分為兩個階段,一個是冒泡階段,另一個則是捕獲階段.整個事件流當中,先執行捕獲,後執行冒泡.上面事例代碼執行結果的原因就是事件是在冒泡階段執行的(w3c在注冊事件當中規定預設情況下在冒泡階段執行).

在冒泡的階段的執行順序是按照 :從事件源 —>document 依次執行的

在剛才的例子當中的展現則是 son —>far(由内到外)

而捕獲階段則是: document—>事件源 依次執行

在下面例子中展現則是far—>son(從外到内)

//js es6
    let far = document.getElementById('far');
    let son = document.getElementById('son');
    far.addEventListener('click',()=>console.log('我是far'),true);
    son.addEventListener('click',()=>console.log('我是son'));

    //addEventListenner函數的第三個參數可以控制事件在冒泡/捕獲階段執行. 當為false是,在冒泡階段執行,為true時,則在捕獲階段執行.
           
js中的冒泡,捕獲以及事件委托

以上就是所謂的冒泡和捕獲.

為了更好的了解前面所說的内容,我們來通過一段代碼分析來加深了解

<ul id="ul">
        <li id='one'>222</li>
        <li id='two'><a href="#" id='only'>111</a></li>
        <li id='three'>333</li>
    </ul>
           
const ul = document.getElementById('ul');
    const li = document.getElementById('two');
    const a = document.getElementById('only')

    a.addEventListener(
        'click',
        ()=>console.log('我是a标簽'),
        false);//冒泡階段執行

    li.addEventListener(
        'click', 
        ()=>console.log('我是li标簽'),
        false);//冒泡階段執行

    ul.addEventListener(
        'click',
        ()=>console.log('我是ul标簽'),
        true);//捕獲階段執行
           

從以上代碼可以看出來, 三個dom元素具有子父級的關系

ul > li > a

當我點選a标簽時,執行結果如下:

ul —> a —>li

js中的冒泡,捕獲以及事件委托

代碼分析:

因為我手動設定ul在捕獲階段執行,li 和 a 在冒泡階段執行.而前面我們講過,事件流是先執行捕獲後執行冒泡的.

說到這裡,相信大家已經能夠清晰的了解什麼是冒泡和捕獲了吧.

事件委托

我們來假設一個場景,我要給一個ul清單中的每一個li設定點選事件,應該怎麼做呢?

一般做法 : 就是給每一個li添加一個點選事件. 但是這樣做的弊端較大,備援代碼多,占用記憶體多等問題.

更好的辦法 : 使用事件委托. 利用我們前面說到的,給父元素注冊點選事件,點選内部子元素時,也會觸發父元素的事件.代碼如下:

<ul id="ul">
        <li id='one'>111</li>
        <li id='two'>222</li>
        <li id='three'>333</li>
    </ul>
           
let ul = document.getElementById('ul');

    ul.addEventListener('click',(e)=>{
        let ev = e || window.event; 
        let tag = ev.target||ev.srcElement;
        switch(tag.id){
            case 'one':
                console.log("one")
            break;
            case 'two':
                console.log('two')
            break;
            case 'three':
                console.log('three')
            break;
        }
    },true);
           

注:

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

繼續閱讀