不使用jQuery,防止在悬停在父绝对div的子元素上时触发onmouseout事件

227
我在绝对定位的 div 中使用 onmouseout 函数时遇到了问题。当鼠标点击 div 中的子元素时,会触发 mouseout 事件,但我希望只有鼠标移出父 div 后才触发该事件。
我应该如何防止鼠标点击子元素时触发 mouseout 事件,而不使用 jQuery 呢?我知道这与事件冒泡有关,但是我找不到解决方法。
我在这里找到了一个类似的帖子:如何禁用由子元素触发的 mouseout 事件? 但是该解决方案使用了 jQuery。

1
我最终使用超时解决了这个问题,并在子元素悬停时清除它。 - John
嗨,我看了你的演示,但当我在右下面板中悬停在元素上时,什么也没有发生? - John
1
如果您正在寻找一种方法来防止鼠标悬停在子元素上时触发父元素事件的mouseout,可以参考以下答案:javascript mouseover/mouseout - user823527
1
请查看@Zach Saucier的回答(下面有链接):https://dev59.com/J2445IYBdhLWcg3wytIU#18837002 - craigmichaelmartin
23个回答

184

使用 onmouseleave

或者,在jQuery中使用 mouseleave()

这正是你所寻找的东西。 例如:

<div class="outer" onmouseleave="yourFunction()">
    <div class="inner">
    </div>
</div>

或者,在jQuery中:

$(".outer").mouseleave(function(){
    //your code here
});

一个示例在这里


3
我最终也使用了 onMouseLeave,因为它不会冒泡。 - Alecz
节省了我很多时间。谢谢。 - npo
mouseleave 无法被取消。 - user11910832
@grecdev,说得更多! - Ben Wheeler

134

如果需要一个更简单的纯CSS方案,在大多数情况下,可以通过将子元素的pointer-events设置为none来实现。

.parent * {
     pointer-events: none;
}

浏览器支持: IE11+


4
太棒了!完全解决了我的问题! - Anna_MediaGirl
4
应该是第一或至少第二个答案,我已经准备好使用事件监听器来解决问题,但这个方法也完全解决了我的问题。太简单了! - Mickael V.
22
这会防止“mouseout”事件被触发,但对我来说也会防止实际子菜单项被点击选择,而这正是菜单的目的。 - johnbakers
@hellofunk 这就是为什么你要将点击事件应用于父元素。所需的确切代码因实现而异,本答案只建议使用 pointer-events 属性。 - Zach Saucier
2
只有在区域内没有其他控制器元素(如编辑、删除)时,此解决方案才有效,因为它也会阻止它们。 - Zoltán Süle
显示剩余4条评论

102
function onMouseOut(event) {
        //this is the original element the event handler was assigned to
        var e = event.toElement || event.relatedTarget;
        if (e.parentNode == this || e == this) {
           return;
        }
    alert('MouseOut');
    // handle mouse event here!
}



document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);

我做了一个快速的 JsFiddle 演示,包含所有需要的 CSS 和 HTML,请查看...

编辑 为了跨浏览器支持,修复了链接http://jsfiddle.net/RH3tA/9/

注意 它只检查直接父元素,如果父 div 有嵌套的子元素,您必须通过遍历它们的父元素来寻找 "原始元素"

编辑 嵌套子元素的示例

编辑 为了适应各种浏览器,进行了修正

function makeMouseOutFn(elem){
    var list = traverseChildren(elem);
    return function onMouseOut(event) {
        var e = event.toElement || event.relatedTarget;
        if (!!~list.indexOf(e)) {
            return;
        }
        alert('MouseOut');
        // handle mouse event here!
    };
}

//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);

//quick and dirty DFS children traversal, 
function traverseChildren(elem){
    var children = [];
    var q = [];
    q.push(elem);
    while (q.length > 0) {
      var elem = q.pop();
      children.push(elem);
      pushAll(elem.children);
    }
    function pushAll(elemArray){
      for(var i=0; i < elemArray.length; i++) {
        q.push(elemArray[i]);
      }
    }
    return children;
}

这里有一个新的JSFiddle编辑更新了链接。


1
你应该在代码片段中添加一个mouseout事件,以便进行测试。可以使用警告框或其他方法。 - John
2
如果您有孙子或曾孙,您能否使用递归解决方案而不仅仅是直接的子代? - Roozbeh15
顺便提一下,如果您想使用innerHTML在Javascript中创建子元素,这是行不通的。它们必须使用createElement方法创建,并且在事件添加到父元素之前创建。 - Guesser
28
这个不起作用。 mouseleave 是正确的答案。 - Code Whisperer
1
这是一个相当老的答案 - 现在你可以使用 contains,这使得它成为一个更加优雅的解决方案:function makeMouseOutFn(event) { e = event.toElement || event.relatedTarget; if (this.contains(e)) return; //handle mouse event here } - Ian
显示剩余6条评论

35

使用onmouseleave而不是onmouseout。

由于您没有向我们展示您的具体代码,因此我无法向您展示如何在您的特定示例中完成它。

但这非常简单:只需将onmouseout替换为onmouseleave。

就这样 :) 如此简单 :)

如果不确定如何操作,请参见以下解释:

https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out

小菜一碟 :) 尽情享受吧 :)


这是一个很好的提示,如果你遇到类似的情况,一定要好好检查一下。 - Chris
这对我来说完美无瑕!我有一个带有两个按钮的 div,在悬停父元素时会显示出来。谢谢! - Italo Borges

29

以下是一种更优雅的解决方案,基于以下内容。 它考虑了来自多个子级别的事件冒泡。 它也考虑到了跨浏览器问题。

function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
   var e = event.toElement || event.relatedTarget;

//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
    if (e.parentNode == this||  e == this) {
        if(e.preventDefault) e.preventDefault();
        return false;
    }
    e = e.parentNode;
}

//Do something u need here
}

document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);

Sam Elie,这个脚本在IE、Safari或Opera上似乎无法工作。你有一个跨浏览器版本的脚本吗?它在Firefox和Chrome中运行得很好。谢谢。 - user352353
1
当我将上述函数粘贴到我的脚本中时,Dreamweaver和FF在第一行上抛出一个错误,“function onMouseOut(this, event) {”。看起来你不能将“this”作为参数放入其中。如果我从输入参数中省略“this”,它似乎有点起作用,但我不确定,因为“我不知道我不知道的东西”。 - TARKUS
1
我在Chrome上遇到了同样的问题,出现在相同的行上,但是从函数定义行中排除this可以解决这个问题。另外,为了更好的跨浏览器支持,请尝试以下代码: if (window.addEventListener){ document.getElementById('parent').addEventListener('mouseout',onMouseOut,true); } else if (window.attachEvent){ document.getElementById('parent').attachEvent("onmouseout",onMouseOut); } - DWils
非常帅气!如果需要支持旧版浏览器,则是最佳答案。 - BurninLeo

15

如果你正在使用jQuery,你也可以使用"mouseleave"函数,它会为你处理所有这些问题。

$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);

当鼠标进入目标div或其任何子元素时,do_something将触发,只有当鼠标离开目标div和其任何子元素时,do_something_else才会触发。


14

感谢启发我的Amjad Masad。

我有以下解决方案,似乎在IE9、Firefox和Chrome中都能正常工作,代码相当简短(没有复杂的闭包和遍历子元素的操作):

    DIV.onmouseout=function(e){
        // check and loop relatedTarget.parentNode
        // ignore event triggered mouse overing any child element or leaving itself
        var obj=e.relatedTarget;
        while(obj!=null){
            if(obj==this){
                return;
            }
            obj=obj.parentNode;
        }
        // now perform the actual action you want to do only when mouse is leaving the DIV
    }

这个是赢家。 - Spencer Williams

7
尝试使用 mouseleave() 方法。
示例:
<div id="parent" mouseleave="function">
   <div id="child">

   </div>
</div>

;)


7
我认为Quirksmode能够回答你所有需要的问题(不同浏览器的冒泡行为和mouseenter/mouseleave事件),但我认为在这种事件冒泡混乱的情况下,最常见的解决方法使用类似JQuery或Mootools的框架(它们具有mouseentermouseleave事件,正如您所预期的那样)。
如果您愿意,可以看看他们是如何做到的,自己动手
或者您可以创建您定制的“精简版”Mootools,只包含事件部分(及其依赖项)。

5

我发现了一个非常简单的解决方案,只需使用onmouseleave="myfunc()"事件,而不是onmousout="myfunc()"事件。

在我的代码中,它起作用了!

示例:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It doesn't fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div" >TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

鼠标移出功能的相同示例:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div">TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

希望这有所帮助 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接