问题是这样的,前一段时间需要做一个二级菜单,为了保证浏览器的兼容性,需要用到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>