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等,本身沒有冒泡屬性