Javascript中onmouseout相关问题的分析

问题是这样的,前一段时间需要做一个二级菜单,为了保证浏览器的兼容性,需要用到mouseover和mouseout进行二级菜单的显示和隐藏。当用mouseout从二级菜单的父元素内移动的时候,发现父元素的mouseout事件被触发了很多次,下面是模拟的代码:

<!DOCTYPE HTML>
<html>
<head>
<style>
#parent {width:500px;height:500px;position:absolute;left:50%;top:50%;margin-left:-250px;
margin-top:-250px;background:red;}
#child {width:300px;height:300px;background:blue;margin:100px}
</style>
</head>
<body>
<div id="parent" onmouseout="alert('leave')">
<div id="child">
</div>
</div>	
</body>
</html>


预览

下列情况下,parent的mouseout事件被触发:
1,鼠标离开parent
2,鼠标从child移动到parent
3,鼠标从parent移动到child
那么,我想要的效果是,只要是我的鼠标在parent内(包括后代元素),mouseout事件均不被触发。显然,只有第一点符合我的要求,那么第二点和第三点为什么会触发父元素的mouseout事件?
先看第二点,熟悉事件冒泡的可能知道,DOM事件会随着事件的产生一直从子对象冒泡到顶级对象,如果这个顶层对象上绑定有事件处理句柄,相应的句柄则会被执行。要避免这一点比较简单,直接在子元素上注册一个mouseout的事件处理句柄,用该句柄来阻止事件往上级元素冒泡。当然这样做比较麻烦,因为子元素的数量是不可控的,有可能随时添加子元素,不便于扩展。
再看第三点,这点理解起来应该不难:鼠标从父元素移动到子元素也算是out,会触发mouseout事件。要避免这一点被触发,就必须要判断当前移动到的目标元素是否在父元素内,如果在的话,则不执行句柄或直接返回。

把第二点和第三点的解决办法合起来,直接用第三种的方法,判断当前out是否在对象的对象里,如果不在的话才能执行函数。下面是整个的解决方法,里面涉及到一些兼容性问题,会在注释中一一解释。

<!DOCTYPE HTML>
<html>
<head>
<style>
#parent {width:500px;height:500px;position:absolute;left:50%;top:50%;margin-left:-250px;
margin-top:-250px;background:red;}
#child {width:300px;height:300px;background:blue;margin:100px}
</style>
<script>
function outhandler(e,obj){
  var e=e||window.event;//事件兼容
  var target=e.relatedTarget||e.toElement;//获取鼠标离开时,所在的对象
	if(obj==target||obj.contains(target)){	//如果鼠标在父元素当中,则退出函数
		stopBubble(e);
		return;
	}
	alert("a");
}
function stopBubble(e){
	if (e && e.stopPropagation) {//非IE浏览器  
		 e.stopPropagation();    
	}    
	else {//IE浏览器
		window.event.cancelBubble = true;   
}   

}
</script>
</head>
<body>
<div id="parent" onmouseout="outhandler(event,this)">
<div id="child">
</div>
</div>	
</body>
</html>

预览

发表回复