JS中的事件冒泡问题
onMouseOver,onMouseOut,onClick等都存在事件冒泡
比如代码如下:
<div onClick="alert('你点了div');">
<input type="button" value="div中的按钮" onClick="alert('你点了button')" />
</div>
<input type="button" value="div中的按钮" onClick="alert('你点了button')" />
</div>
div中有一个button
事实是,当你单击了按钮,div的onClick事件也会执行,不单是这样,所有button的父级对像都会执行onClick包括body, 这显然不是我们想要的.
解决办法:
阻止事件冒泡
function zuzhi(e){
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
上面的代码就成了这样:
<script type="text/javascript">
function zuzhi(e){
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
<div onClick="alert('你点了div');">
<input type="button" value="div中的按钮" onClick="(function(e){alert('你点了button');zuzhi(e);})()" />
</div>
function zuzhi(e){
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
<div onClick="alert('你点了div');">
<input type="button" value="div中的按钮" onClick="(function(e){alert('你点了button');zuzhi(e);})()" />
</div>
上面说的是onClick事件,子对像阻止了事件冒泡,但要是父对像的事件,子对像触发怎么怎么办泥?
还是拿一个div和一个button打比方:
<div onMouseOver="document.getElementById('msg').innerHTML+='\r我进来了'" onMouseOut="document.getElementById('msg').innerHTML+='\r我出去了'">
<input type="button" value="按钮" />
</div>
<div id="msg"></div><!--这里只是用记录事件执行的-->
<input type="button" value="按钮" />
</div>
<div id="msg"></div><!--这里只是用记录事件执行的-->
本来很明了的事情也出错了,你会发鼠标移入button上时候先后执行VID的onMouseOut和onMouseOver事件,其了怪了,这管button什么事.这就是事件冒泡.
这时候我也和处理onClick事件一样给button的onMouseOut 和 onMouseOver事件组织事件冒泡:
<script type="text/javascript">
function zuzhi(e){
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
function zuzhi(e){
if (e && e.stopPropagation ) { //如果提供了事件对象,则这是一个非IE浏览器
e.stopPropagation();
}else{//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
<div onMouseOver="document.getElementById('msg').innerHTML+='\r我进来了'" onMouseOut="document.getElementById('msg').innerHTML+='\r我出去了'">
<input type="button" value="按钮" onMouseOut="(function(e){zuzhi(e);})();" onMouseOver="(function(e){zuzhi(e);})();" />
</div>
<div id="msg"></div><!--这里只是用记录事件执行的-->
}
}
</script>
<div onMouseOver="document.getElementById('msg').innerHTML+='\r我进来了'" onMouseOut="document.getElementById('msg').innerHTML+='\r我出去了'">
<input type="button" value="按钮" onMouseOut="(function(e){zuzhi(e);})();" onMouseOver="(function(e){zuzhi(e);})();" />
</div>
<div id="msg"></div><!--这里只是用记录事件执行的-->
啊哦,问题来了,当鼠标进入button时执行了div的onMouseOut,而鼠标出button时执行了div的onMouseOver,头都大了,怎么办?
实验说明当鼠标进入子标签的一定会执行onMouseOut事件,和子事件冒泡似乎无关了.那怎么解决呢.
在这个情况下我两种方法:
1.给onMouseOut执行语句时加个延迟执行.不阻止子对像的事件冒泡,这样操作简单,可能很多效果本事就会用到延迟执行.
这样改了之后你会发现,无论你把鼠标移进按钮还是移出按钮,执行的都好像只执行了 onMouseOver 事件; 有时候虽然多次执行onMouseOver但关不改变什么,就能用这个方法解决;
<script type="text/javascript">
var $in, $out;
function M_in() {
clearTimeout($out);
$in= setTimeout(function() {document.getElementById('msg').innerHTML +='\r我进来了'},100)
}
function M_out() {
clearTimeout($in);
$out = setTimeout(function() {document.getElementById('msg').innerHTML +='\r我出去了'},100)
}
</script>
<div onMouseOver="M_in();" onMouseOut="M_out();">
<input type="button" value="按钮"/>
</div>
<div id="msg">
</div><!--这里只是用记录事件执行的-->
var $in, $out;
function M_in() {
clearTimeout($out);
$in= setTimeout(function() {document.getElementById('msg').innerHTML +='\r我进来了'},100)
}
function M_out() {
clearTimeout($in);
$out = setTimeout(function() {document.getElementById('msg').innerHTML +='\r我出去了'},100)
}
</script>
<div onMouseOver="M_in();" onMouseOut="M_out();">
<input type="button" value="按钮"/>
</div>
<div id="msg">
</div><!--这里只是用记录事件执行的-->
2.执行时判断事件对像是子对像触发,是就停止执行;
IE和其它浏览器用的方法不一样,所以代码会多一点(经过IE6789,FF,Chrome测试);
<script type="text/javascript">
function M_out(e) {
var obj = document.getElementById('fff');
if (window.event && !e.relatedTarget) {
if (obj.contains(event.toElement)) {
return
}
} else if (e && e.relatedTarget) {
if (obj.contains(e.relatedTarget)) {
return
}
};
document.getElementById('msg').innerHTML += '我出去了<br/>'
}
function M_in(e) {
var obj = document.getElementById('fff');
if (window.event && !e.relatedTarget) {
if (obj.contains(event.fromElement)) {
return
}
} else if (e && e.relatedTarget) {
if (obj.contains(e.relatedTarget)) {
return
}
};
document.getElementById('msg').innerHTML += '我进来了<br/>'
}
</script>
<div id="fff" onMouseOver="M_in(event);" onMouseOut="M_out(event);" style="padding:10px; margin:0 auto; width:700px; border:3px solid #00F">
<input id="bt1" type="button" value="按钮" />
</div>
<div id="msg" style="padding:5px;border:1px solid #00F">
</div><!--这里只是用记录事件执行的-->
function M_out(e) {
var obj = document.getElementById('fff');
if (window.event && !e.relatedTarget) {
if (obj.contains(event.toElement)) {
return
}
} else if (e && e.relatedTarget) {
if (obj.contains(e.relatedTarget)) {
return
}
};
document.getElementById('msg').innerHTML += '我出去了<br/>'
}
function M_in(e) {
var obj = document.getElementById('fff');
if (window.event && !e.relatedTarget) {
if (obj.contains(event.fromElement)) {
return
}
} else if (e && e.relatedTarget) {
if (obj.contains(e.relatedTarget)) {
return
}
};
document.getElementById('msg').innerHTML += '我进来了<br/>'
}
</script>
<div id="fff" onMouseOver="M_in(event);" onMouseOut="M_out(event);" style="padding:10px; margin:0 auto; width:700px; border:3px solid #00F">
<input id="bt1" type="button" value="按钮" />
</div>
<div id="msg" style="padding:5px;border:1px solid #00F">
</div><!--这里只是用记录事件执行的-->
说明:mouseover中relatedTarget指向鼠标来自的元素,而mouseout中的relatedTarget指向的是鼠标去向的那个元素。IE中则是toElement和fromElement;
如果直接用JS的事件绑定,判断上会简单很多:
<script type="text/javascript">
window.onload = function() {
var obj = document.getElementById("fff");
var msg = document.getElementById("msg");
obj.onmouseout = function(e) {
if (e) {
if (obj.contains(e.relatedTarget)) {
return
}
} else if (event) {
if (obj.contains(event.toElement)) {
return
}
};
msg.innerHTML += '我出去了<br/>'
}
obj.onmouseover = function(e) {
if (e) {
if (obj.contains(e.relatedTarget)) {
return
}
} else if (event) {
if (obj.contains(event.fromElement)) {
return
}
};
msg.innerHTML += '我进来了<br/>'
}
}
</script>
<div id="fff" style="padding:10px; margin:0 auto; width:700px; border:3px solid #00F">
<input id="bt1" type="button" value="按钮" />
</div>
<div id="msg" style="padding:5px;border:1px solid #00F">
</div><!--这里只是用记录事件执行的-->
window.onload = function() {
var obj = document.getElementById("fff");
var msg = document.getElementById("msg");
obj.onmouseout = function(e) {
if (e) {
if (obj.contains(e.relatedTarget)) {
return
}
} else if (event) {
if (obj.contains(event.toElement)) {
return
}
};
msg.innerHTML += '我出去了<br/>'
}
obj.onmouseover = function(e) {
if (e) {
if (obj.contains(e.relatedTarget)) {
return
}
} else if (event) {
if (obj.contains(event.fromElement)) {
return
}
};
msg.innerHTML += '我进来了<br/>'
}
}
</script>
<div id="fff" style="padding:10px; margin:0 auto; width:700px; border:3px solid #00F">
<input id="bt1" type="button" value="按钮" />
</div>
<div id="msg" style="padding:5px;border:1px solid #00F">
</div><!--这里只是用记录事件执行的-->
补充
1. 不是所有的事件都能冒泡。以下事件不冒泡:blur、focus、load、unload。
2.阻止冒泡并不能阻止对象默认行为。比如submit按钮被点击后会提交表单数据,这种行为无须我们写程序定制。