- 什么是事件委托?
- 怎么阻止默认动作?
- 怎么阻止事件冒泡?
1.什么是事件委托?
事件委托也称之为事件代理(Event Delegation)。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
举例:比如一个宿舍的同学同时快递到了,一种方法就是他们一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一
一分发给每个宿舍同学; 在这里,取快递就是一个事件,每个同学指的是需要响应事件的
DOM
元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。
一个事件触发后,会在子元素和父元素之间传播(propagation),
这种传播分成三个阶段。
- 捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段不会响应任何事件;
- 目标阶段:在目标节点上触发,称为“目标阶段”
- 冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling
phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层
事件委托的优点:
【1】可以大量节省内存占用,减少事件注册。比如在ul上代理所有li的click事件就非常棒
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
如上面代码所示,如果给每个li列表项都绑定一个函数,那对内存的消耗是非常大的,因此较好的解决办法就是将li元素的点击事件绑定到它的父元素ul身上,执行事件的时候再去匹配判断目标元素。
【2】可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
假设上述的例子中列表项li就几个,我们给每个列表项都绑定了事件; 在很多时候,我们需要通过 AJAX
或者用户操作动态的增加或者删除列表项li元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
使用事件委托注意事项:使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。
2.怎么阻止默认动作?
有一些html元素默认的行为,比如说a标签,点击后有跳转动作;form表单中的submit类型的input有一个默认提交跳转事件;reset类型的input有重置表单行为。
如果你想阻止这些浏览器默认行为,JavaScript为你提供了方法。
如下代码:
var $a = document.getElementsByTagName("a")[0];
$a.onclick = function(e){
alert("跳转动作被我阻止了")
e.preventDefault();
//return false;//也可以
}
默认事件没有了。
既然return false 和 e.preventDefault()都是一样的效果,那它们有区别吗?当然有。 仅仅是在HTML事件属性 和
DOM0级事件处理方法中,才能通过返回 return false 的形式组织事件宿主的默认行为。
w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false;
在支持 addEventListener() 的浏览器中,也能通过调用时间对象的 preventDefault()
方法取消时间的默认操作。不过,在 IE9 之前的 IE 中,可以通过设置事件对象的 returnValue 属性为 false
来达到同样的效果。下面的代码假设一个事件处理程序,它使用全部的三种取消技术:
function cancelHandler(event){
var event = event || window.event; //用于IE
if(event.preventDefault) event.preventDefault(); //标准技术
if(event.returnValue) event.returnValue = false; //IE
return false; //用于处理使用对象属性注册的处理程序
}
当前的 DOM 事件模型草案定义了 Event 对象属性 defaultPrevented。
return false
javascript 的 return false 只会阻止默认行为,用 jQuery 的话既可以阻止默认行为又可以防止对象冒泡。
下面这个使用原生 JS,只会阻止默认行为,不会停止冒泡:
<div id='div' onclick='alert("div");'>
<ul onclick='alert("ul");'>
<li id='ul-a' onclick='alert("li");'>
<a href="http://caibaojian.com/" id="testB" >caibaojian.com</a>
</li>
</ul>
</div>
var a = document.getElementById("testB");
a.onclick = function(){
return false;
}
3.怎么阻止冒泡事件
w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true
function stopBubble(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
}
在支持 addEventListener() 的浏览器中,可以调用事件对象的一个 stopPropagation()
方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation()
方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
IE9 之前的IE不支持 stopPropagation() 方法。相反,IE事件对象有一个 cancleBubble 属性,设置这个属性为
true 能阻止事件进一步传播。( IE8 及之前版本不支持事件传播的捕获阶段,所以冒泡是唯一待取消的事件传播。)
当前的 DOM 事件规范草案在 Event 对象上定义了另一个方法,命名为stopImmediatePropagation()。类似
stopPropagation(),这个方法组织了任何其他对象的事件传播,但也阻止了在相同对象上注册的任何其他事件处理程序的调用。
<div id='div' onclick='alert("div");'>
<ul onclick='alert("ul");'>
<li onclick='alert("li");'>test</li>
</ul>
</div>
function stopHandler(event)
window.event?window.event.cancelBubble=true:event.stopPropagation();
}