<div id="outer">
<p id="inner">Click me!</p>
</div>
事件冒泡
微软提出了名为事件冒泡(event bubbling)的事件流。时间冒泡就是指事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是p -> div -> body -> html -> document
事件捕获
网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
上面的例子在事件捕获的概念下发生click事件的顺序应该是document -> html -> body -> div -> p
Netscape Navigator不会将页面上的很多元素暴漏给事件
在DOM事件流中,“DOM2级事件”中规定的事件流同时支持了事件捕获阶段和事件冒泡阶段
穿插一个小片段,解释一下这里的dom2级事件。
使用事件处理程序有三种方式:
1、HTML事件处理程序
如:<div id="outer">
<p id="inner" onclick="alert("我是html事件处理程序");">Click me!</p>
</div>
2、DOM0级事件处理程序
如:<div id="outer"> <p id="inner">Click me!</p> </div><script type="text/javascript">var pBtn=document.get
ElementById("inner");
pBtn.onclick=function(){}
</script>
3、DOM2级事件
就是用addEventListener()等类型的,在事件处理上IE和DOM也是有区别的,
IE中绑定事件和解绑为attachEvent(),detachEvent(),
DOM中的为addEventListener(),removeEventListener()
那么如何兼容此问题呢?如下代码,通过if..else类型判断
<body> <div id="box"> <input type="button" value="按钮" id="btn" onclick="showMes()"> <input type="button" value="按钮2" id="btn2"> <input type="button" value="按钮3" id="btn3"> <a href="event.html" id="go">跳转</a> </div> </body>
html
var eventUtil={ // 添加句柄 addHandler:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); }else if(element.attachEvent){ element.attachEvent(‘on‘+type,handler); }else{ element[‘on‘+type]=handler; } }, // 删除句柄 removeHandler:function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false); }else if(element.detachEvent){ element.detachEvent(‘on‘+type,handler); }else{ element[‘on‘+type]=null; } }, getEvent:function(event){ return event?event:window.event; }, getType:function(event){ return event.type; }, getElement:function(event){ return event.target || event.srcElement; }, preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue=false; } }, stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble=true; } } }
js
window.onload=function(){ var go=document.getElementById(‘go‘), box=document.getElementById(‘box‘); eventUtil.addHandler(box,‘click‘,function(){ alert(‘我是整个父盒子‘); }); eventUtil.addHandler(go,‘click‘,function(e){ //e=eventUtil.getEvent(e); e=e || window.event; alert(eventUtil.getElement(e).nodeName); eventUtil.preventDefault(e); eventUtil.stopPropagation(e); }); }
js
应用例子:
<ul id="color-list">
<li>red</li>
<li>yellow</li>
<li>blue</li>
<li>green</li>
<li>black</li>
<li>white</li>
</ul>
如果点击页面中的li元素,然后输出li当中的颜色,我们通常会这样写:
(function(){
var color_list = document.getElementById(‘color-list‘);
var colors = color_list.getElementsByTagName(‘li‘);
for(var i=0;i<colors.length;i++){ colors[i].addEventListener(‘click‘,showColor,false);
};
function showColor(e){
var x = e.target;
alert("The color is " + x.innerHTML);
};
})();
这样的代码再正常不过了,但是,如果页面上有几百个元素需要绑定(假设),那么务必就要绑定几百次啦。
这样问题就出现了:
第一:大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏) 第二:绑定的元素必须要存在 第三: 后期生成HTML会没有事件绑定,需要重新绑定 第四: 语法过于繁杂
在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。
利用事件流的特性,我们只绑定一个事件处理函数也可以完成:
(function(){
var color_list = document.getElementById(‘color-list‘);
color_list.addEventListener(‘click‘,showColor,false);
function showColor(e){
var x = e.target;
if(x.nodeName.toLowerCase() === ‘li‘){
alert(‘The color is ‘ + x.innerHTML);
}
}
})();
使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。假如上述列表元素当中添加了其他的元素(如:a、span等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。
在Jquery中如何用呢?
具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document)
委托这么好的特性 jQuery 当然不会放过,所以就衍生出 .bind()、.live() .on()和.delegate(),jQuery 的事件绑定有多个方法可以调用,以 click 事件来举例:
click方法 bind方法 delegate方法 on方法
这里要清楚的认识:不管你用的是(click / bind / delegate)之中哪个方法,最终都是 jQuery 底层都是调用 on 方法来完成最终的事件绑定。因此从某种角度来讲除了在书写的方便程度及习惯上挑选,不如直接都采用 on 方法来的痛快和直接。
所以在新版的 API 中都这么写到:
.on()方法事件处理程序到当前选定的 jQuery 对象中的元素。在jQuery 1.7中,.on()方法提供绑定事件处理的所有功能、效果不言而喻了,除了性能的差异,通过委托的事件还能很友好的支持动态绑定,只要 on 的delegate 象是 HTML 页面原有的元素,由于是事件的触发是通过Javascript的事件冒泡机制来监测,所以对于所有子元素(包括后期通过JS生成的元素)所有的事件监测均能有效,且由于不用对多个元素进行事件绑定,能够有效的节省内存的损耗。