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