事件冒泡與事件委託筆記

1,事件冒泡:當一個子元素的事件被觸發的時候(如onclick事件),該事件會從事件源(被點擊的子元素)開始逐級向上傳播,觸發父級元素的點擊事件

    子元素在沒有定義具體的click處理函數的時候,仍然可以冒泡觸發父元素的click事件

    只有相應的事件會發生事件冒泡,不想管的事件不受影響(注:由於click爲鼠標的點擊,所以同樣會觸發mousedown與mouseup等相關事件,同時發生冒泡)

    阻止事件冒泡:在事件觸發時,會傳入一個相應的event對象,其中有一個stopPropagation()可以阻止事件冒泡(IE中爲cancleBubble=true)

 

2,事件委託:將子元素的事件通過冒泡的形式交給父元素來執行

    事件委託可以在遍歷每一個欄目的時候,減少DOM操作次數

    讓事件效果只有點擊事件源纔會觸發,標準瀏覽器使用ev.target,IE瀏覽器用event.srcElement

    例:只有點擊li會觸發事件,且每次只執行一次dom操作

       window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(target.innerHTML);
    }
  }
}

    上面這個例子是所有的li執行同樣的效果,要是每個li被點擊的效果都不一樣,也可以優化:

    例:

    window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('刪除');
                            break;
                        case 'move' :
                            alert('移動');
                            break;
                        case 'select' :
                            alert('選擇');
                            break;
                    }
                }
            }
            
        }

    但是還有一個問題,當新添加節點時,新增的節點是沒有事件的

    例:window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //鼠標移入變紅,移出變白
            for(var i=0; i<aLi.length;i++){
                aLi[i].onmouseover = function(){
                    this.style.background = 'red';
                };
                aLi[i].onmouseout = function(){
                    this.style.background = '#fff';
                }
            }
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }

    解決辦法:可以用函數將事件包起來,在新增節點之後調用,例:

    window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            function mHover () {
                //鼠標移入變紅,移出變白
                for(var i=0; i<aLi.length;i++){
                    aLi[i].onmouseover = function(){
                        this.style.background = 'red';
                    };
                    aLi[i].onmouseout = function(){
                        this.style.background = '#fff';
                    }
                }
            }
            mHover ();
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
                mHover ();
            };
        }

    再用事件委託,優化如下:

    window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //事件委託,添加的子元素也有事件
            oUl.onmouseover = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "red";
                }
                
            };
            oUl.onmouseout = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }
                
            };
            
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }

    說明,當用事件委託時,不需要遍歷元素的子節點,只需要給父級元素添加事件就好了,其他的都在js裏面執行,這樣可以大大減少dom操作,這是事件委託的精髓。

    再給一個場景 ul > li > div > p,當div佔滿li,p佔滿div,還是給ul綁定事件,需要判斷點擊的是否爲li,代碼如下:

    

<ul id="test">
        <li>
            <p>11111111111</p>
        </li>
        <li>
            <div>
                22222222
            </div>
        </li>
        <li>
            <span>3333333333</span>
        </li>
        <li>4444444</li>
    </ul>

    解決辦法,核心在於while循環,遞歸調用,也可以寫成一個函數,用遞歸的方法來調用,同時用到冒泡原理,直到currentTarget爲止,噹噹前的target爲li時,就可以執行對應事件了

    

var oUl = document.getElementById('test');
    oUl.addEventListener('click',function(ev){
        var target = ev.target;
        while(target !== oUl ){
            if(target.tagName.toLowerCase() == 'li'){
                console.log('li click~');
                break;
            }
            target = target.parentNode;
        }
    })

    總結:

    適合用事件委託的事件:click,mousedown,mouseup,keydown,keyup,keypress

    而mouseover和mouseout雖然也有事件冒泡,但在處理它們的時候,需要經常計算它們的位置,處理起來不太容易

    不適合的就很多,比如focus blur等,本身沒有冒泡屬性

 

 

 

 

 

    

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章