什么是事件冒泡和事件委托
自从有了javaScript这个浏览器端的脚本语言,用户们终于可以告别单调的展示网页了,javaScript给网页带来了新的生命力,网页前端从此变得有趣起来,最主要的,网页上的元素有了事件这个元素,但是关于事件,就不得不提事件冒泡和事件委托这两个关键的知识点。
¶什么是事件?
JavaScript与html之间的交互全部都是由事件实现的。事件就是你操作鼠标或者键盘,对网页进行交互,比如你点击一个按钮,就触发了一个onClick事件,你改变input节点内的value,就出发了onChange事件。
¶事件流和事件冒泡
你可以把网页想象成几个同心圆,当你点击最中心的圆的时候,也相当与点击外层的几个圆了,所以说,当你点击页面上的一个按钮,就相当于点击了整张页面,会触发这个按钮的所有父节点的click事件,这个就是事件流。但是当时ie和其他的浏览器偏偏要不一样,ie支持的是冒泡流,而网景提出的是捕获流,后来,在w3c组织的统一下,js支持冒泡流和捕获流,但是呢,某些低版本的浏览器(ie6,ie7,ie8)却只支持冒泡流。直接用onClick就是在冒泡阶段添加事件。
<div in="outer">
<div id="inner"></div>
</div>
对于事件捕获来说,是从外到内的,点击#inner的div,会从window开始捕获,然后是document,html,…,一直到#outer,再到#inner。
对于事件冒泡来说,是从内到外的,点击#inner的div,会从#inner开始,到#outer,…,最后是window。
dom事件流分为三个阶段
1.事件捕获。
2.处于目标阶段。
3.事件冒泡。
可以分别给dom元素添加事件流事件
document.addEventListener('click',function(){
console.log('document处于事件捕获阶段');
}, true);// true是捕获阶段,false是冒泡阶段。
问题来了,如果不想事件冒泡怎么办?我们点击子节点,不想触发父节点的事件。可以这样做:
stopPropagation() 方法
终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。
¶事件委托
先说下event对象,这个对象会作为事件的默认参数。
api:
bubbles 返回布尔值,指示事件是否是起泡事件类型。
cancelable 返回布尔值,指示事件是否可拥可取消的默认动作。
currentTarget 返回其事件监听器触发该事件的元素。
eventPhase 返回事件传播的当前阶段。
target 返回触发此事件的元素(事件的目标节点)。
timeStamp 返回事件生成的日期和时间。
type 返回当前 Event 对象表示的事件的名称。
为啥要进行事件委托,我们都知道dom操作是很花费时间的。与dom节点交互,访问dom节点,非常影响性能。比如要给ul下的li都添加上click事件,最简单的方法,写个for循环,挨个click就行了,但是这样要多次操纵dom节点。利用事件委托的方式,把click事件绑定到ul上,因为事件冒泡的存在,会触发ul的click事件,而且就一次dom操作,那怎么判断点击的是哪一个li呢?这时候上面的event对象就起作用了,event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement。
这时候我们就拿到了具体点击的dom节点,然后用nodeName获取节点名,在函数内部判断下,就实现把dom操作放在js代码内部来实现了。
而且这样做还有一点好处就是,新添加的li标签不会点击无效,如果直接把事件绑定在li上,每添加li都要重新绑定事件。
<ul id="ul">
<li></li>
</ul>
window.onload = function(){
var oUl = document.getElementById("ul");
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);
}
}
}