我在绝对定位的div中的onmouseout函数遇到了麻烦。当鼠标击中div中的子元素时,mouseout事件发生,但我不希望它发生,直到鼠标离开父元素,绝对div。

我如何防止mouseout事件从发射时,它击中一个子元素没有jquery。

我知道这与事件冒泡有关,但我没有找到如何解决这个问题的方法。

我在这里找到了一个类似的帖子:如何禁用由子元素触发的鼠标退出事件?

但是,该解决方案使用jQuery。


当前回答

香草也可以用这种方法

document.querySelector('.product_items') && document.querySelector('.product_items').addEventListener('mouseleave', () => updateCart()) const updateCart = () => { let total = 0; document.querySelectorAll('input') && document.querySelectorAll('input').forEach(item => total += +item.value) document.getElementById('total').innerHTML = total } <div class="product_items"> <div class="product_item"> <div class="product_name"> </div> <div class="multiply__btn"> <button type="button">-</button> <input name="test" type="text"> <button type="button">+</button> </div> </div> <div class="product_item"> <div class="product_name"> </div> <div class="multiply__btn"> <button type="button">-</button> <input name="test" type="text"> <button type="button">+</button> </div> </div> <div class="product_item"> <div class="product_name"> </div> <div class="multiply__btn"> <button type="button">-</button> <input name="test" type="text"> <button type="button">+</button> </div> </div> </div> <div id="total"></div>

其他回答

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, EDIT更新链接

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

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

Do_something将在鼠标进入targetdiv或其任何子div时触发,do_something_else仅在鼠标离开targetdiv及其任何子div时触发。

我只是想和你分享一些东西。 我在ng-mouseenter和ng-mouseleave事件中遇到了一些困难。

案例分析:

我创建了一个浮动导航菜单,当光标在图标上时切换。 这个菜单在每一页的最上面。

为了处理菜单上的显示/隐藏,我切换了一个类。 ng-class = " {: vm.isHover} " 切换vm。isHover,我使用ng鼠标事件。 ng-mouseenter = " vm。isHover = true" ng-mouseleave = " vm。isHover = false"

目前,一切都很好,并按预期工作。 解决方法简单明了。

接下来的问题是:

在一个特定的视图中,我有一个元素列表。 当光标在列表元素上时,我添加了一个操作面板。 我使用与上面相同的代码来处理该行为。

存在的问题:

我发现当我的光标在浮动导航菜单上,也在一个元素的顶部,有一个相互冲突。 动作面板显示,浮动导航被隐藏。

问题是,即使光标在浮动导航菜单上,列表元素ng-mouseenter也会被触发。 这对我来说毫无意义,因为我认为鼠标传播事件会自动中断。 我必须说我很失望,我花了一些时间来找出那个问题。

第一个想法:

我试着用这些:

$ event.stopPropagation () $ event.stopImmediatePropagation ()

我结合了很多ng指针事件(mouemove, mouveover,…),但没有一个帮助我。

CSS解决方案:

我用一个简单的css属性找到了解决方案,我越来越多地使用它:

pointer-events: none;

基本上,我这样使用它(在我的列表元素上):

ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"

有了这个棘手的,ng-mouse事件将不再被触发,我的浮动导航菜单将不再关闭自己,当光标在它和列表中的一个元素上时。

更进一步说:

正如你所期望的,这个解决方案可行,但我不喜欢它。 我们无法控制事态发展,这很糟糕。 另外,你必须有访问虚拟机的权限。实现这个目标是有可能的,可能是不可能的,但在某种程度上是肮脏的。 如果有人想看的话,我可以做一把小提琴。

然而,我没有其他的解决方案…… 说来话长,我不能给你土豆,所以请原谅我的朋友。 不管怎样,指针事件:没有是生命,所以记住它。

有两种方法可以处理这个问题。

1)检查事件。目标结果在你的回调,看看它是否匹配你的父div

var g_ParentDiv;

function OnMouseOut(event) {
    if (event.target != g_ParentDiv) {
        return;
    }
    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.onmouseout = OnMouseOut;
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>

2)或者使用事件捕获和调用事件。回调函数中的stopPropagation

var g_ParentDiv;

function OnMouseOut(event) {

    event.stopPropagation(); // don't let the event recurse into children

    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>

如果出于某种原因你不想使用mouseenter和mouseleave事件,你可以使用mouseover/mouseout和一些“deboundation”。

这个想法依赖于这样一个事实,即当您的事件处理程序在跨越各个子元素....之间的边界时将接收out,然后接收一个新的over除非鼠标真的离开(超过反弹周期)。这似乎比在每个事件上爬行dom节点更简单。

如果你在假设你有一个真正的out之前有一个短暂的延迟“debounce”,你可以有效地忽略所有这些从子元素冒出来的out/over事件。

注意!如果子元素也有over和/或out事件的监听器,并且它们的处理程序调用event. stoppropagation()来阻止事件向附加处理程序的父元素冒泡,这将不起作用。如果您控制代码,这并不一定是问题,但您应该注意到这一点。

示例代码

javascript



function mouseOverOutDebounce (element, debounceMs, mouseOverFn, mouseOutFn) {
    var over = false,
        debounceTimers = [];

    function mouseOver (evt) {
        if (over) {  // already OVER, existing interaction
            while (debounceTimers.length > 0) { // then we had a pending mouseout(s), cancel
                window.clearTimeout(debounceTimers.shift());
            }
        }
        else { // new OVER
            over = true;
            mouseOverFn(evt);
        }
    }
    function mouseOut (evt) {
        if (!over) return;  // already OUT, ignore.

        debounceTimers.push(window.setTimeout(function () {
            over = false;
            mouseOutFn(evt);
        }, debounceMs));
    }

    function removeEventListeners () {
        element.removeEventListener('mouseover', mouseOver);
        element.removeEventListener('mouseout', mouseOut);
    }
    
    element.addEventListener('mouseover', mouseOver);
    element.addEventListener('mouseout', mouseOut);

    return removeEventListeners;
}

var someEl = document.querySelector('.container'),
    textarea = document.querySelector('textarea'),
    mouseOver = function (evt) { report('mouseOVER', evt); },
    mouseOut = function (evt) { report('mouseOUT', evt); },
    removeEventListeners = mouseOverOutDebounce(someEl, 200, mouseOver, mouseOut);

function report(msg, data) {
    console.log(msg, data);
    textarea.value = textarea.value + msg + '\n';
}

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <style type="text/css">
        html, body {
            margin: 0;
            padding: 0;
        }
        body {
            margin: 5%;
        }
        .container {
            width: 300px;
            height: 600px;
            border: 10px solid red;
            background-color:  #dedede;
            float: left;
        }
        .container .square {
            width: 100px;
            height: 100px;
            background-color: #2086cf;
            margin: -10px 0 0 -10px; 
        }
        textarea {
            margin-left: 50px;
            width: 800px;
            height: 400px;
            background-color: #464646;
            font-family: monospace;
            color: white;
        }
        .bar {
            width: 2px;
            height: 30px;
            display: inline-block;
            margin-left: 2px;
            background-color: pink;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="square"></div>

    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
</div>

<textarea></textarea>


<script src="interactions.js"></script>
</body>
</html>

小提琴

https://jsfiddle.net/matp/9bhjkLct/5/