ChatGPT解决这个技术问题 Extra ChatGPT

在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout

我在使用绝对定位 div 中的 onmouseout 函数时遇到问题。当鼠标点击 div 中的子元素时,mouseout 事件会触发,但我不希望它在鼠标离开父级绝对 div 之前触发。

如何防止 mouseout 事件在没有 jquery 的情况下命中子元素时触发。

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

我在这里找到了类似的帖子:How to disable mouseout events triggered by child elements?

然而,该解决方案使用 jQuery。

我最终使用超时解决了问题,并在子元素悬停时将其清除
嗨,我看过你的演示,但是当我浏览右下角面板中的元素时,没有任何反应?
如果您希望在将鼠标悬停在子元素上时防止父事件鼠标移出,请找到此答案:javascript mouseover/mouseout
查看@Zach Saucier answer buried below

G
Gerry

使用 onmouseleave

或者,在 jQuery 中,使用 mouseleave()

这正是您正在寻找的东西。例子:

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

或者,在 jQuery 中:

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

here 就是一个例子。


onMouseLeave 也是我最终使用的,因为它不会冒泡。
节省了我很多时间。谢谢
mouseleave 无法取消。
@grecdev,说更多!
Z
Zach Saucier

对于在大多数情况下都有效的更简单的纯 CSS 解决方案,可以通过将孩子的 pointer-events 设置为 none 来删除它们

.parent * {
     pointer-events: none;
}

浏览器支持:IE11+


杰出的!完全解决了我的问题!
这应该是第一个或至少是第二个答案,我已经准备好接受事件监听器的麻烦,这也完全解决了我的问题。很简单 !
这可以防止mouseout触发,但对我来说,它也可以防止实际的孩子被点击作为菜单中的选择,这是菜单的重点。
@hellofunk这就是您将点击事件应用于父级的原因。所需的确切代码因实现而异,此答案仅建议使用 pointer-events 属性
它仅在区域内没有其他控制器元素(编辑、删除)时才有效,因为此解决方案也会阻止它们。
c
cнŝdk
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,请查看...

EDIT 跨浏览器支持的固定链接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;
}

还有一个新的 JSFiddleEDIT 更新链接


您应该向小提琴添加一个 mouseout 事件,以便对其进行测试。警报或应该做的事情。
抱歉,您好像有 alert('MouseOut') ,但它对我不起作用?我已经在没有 JS 库选项的情况下运行
如果你有大孩子怎么办?还是曾孙?即你能有一个递归的解决方案,而不是仅仅模仿孩子吗?
这行不通。 mouseleave 是正确答案
这是一个相当古老的答案 - 现在您可以使用 contains 这使它成为一个更优雅的解决方案:function makeMouseOutFn(event) { e = event.toElement || event.relatedTarget; if (this.contains(e)) return; //handle mouse event here }
k
kavi

而不是 onmouseout 使用 onmouseleave。

您没有向我们展示您的具体代码,因此我无法在您的具体示例中向您展示如何做到这一点。

但这很简单:只需将 onmouseout 替换为 onmouseleave 即可。

就是这样:)所以,简单:)

如果不确定如何操作,请参阅以下说明:

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

和平蛋糕:)享受它:)


这是一个伟大的呼喊,如果你有类似的情况,非常值得一试
S
Sam-Elie

这是基于以下内容的更优雅的解决方案。它解释了从多个级别的孩子冒泡的事件。它还解决了跨浏览器问题。

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 中就像一个魅力。谢谢。
当我将上述函数粘贴到我的脚本中时,Dreamweaver 和 FF 在第一行抛出一个错误,“function onMouseOut(this, event) {”。看起来您不允许将“this”作为参数。我想当我从输入参数中省略“this”时它会起作用,但我不确定,因为“我不知道我不知道什么”。
我在同一行的 Chrome 中也弹出了相同的问题,不包括 @GregoryLewis 提到的函数定义行中的 this 解决了这个问题。此外,为了获得更多跨浏览器支持,请尝试以下操作: if (window.addEventListener){ document.getElementById('parent').addEventListener('mouseout',onMouseOut,true); } else if (window.attachEvent){ document.getElementById('parent').attachEvent("onmouseout",onMouseOut); }
很英俊!如果需要对旧版浏览器的支持,则为最佳答案。
J
Jamie Brown

如果您使用 jQuery,您还可以使用“mouseleave”函数,它会为您处理所有这些。

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

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


u
user1431972

感谢 Amjad Masad 启发了我。

我有以下似乎适用于 IE9、FF 和 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
    }

R
Ruben

我认为 Quirksmode 有您需要的所有答案(不同的浏览器冒泡行为和 mouseenter/mouseleave 事件),但我认为该事件冒泡混乱的最常见结论 使用 JQuery 或 Mootools 之类的框架(具有 mouseentermouseleave 事件,这正是您的直觉会发生的事情)。

看看他们是如何做到的,如果您愿意,可以自己做
您可以create your custom仅使用事件部分(及其依赖项)的 Mootools“精益求精”版本。


J
Jean-Philippe

试试mouseleave()

例子 :

<div id="parent" mouseleave="function">
   <div id="child">

   </div>
</div>

;)


A
Arminius

我找到了一个非常简单的解决方案,

只使用 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>

与 mouseout 功能相同的示例:

<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>

希望能帮助到你 :)


M
Mohammed Essehemy

虽然您提到的 solution 使用 jquery,但 mouseentermouseleave 是本机 dom 事件,因此您可以不使用 jquery。


s
selbie

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

1) 检查回调中的 event.target 结果以查看它是否与您的父 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)或者使用事件捕获并在回调函数中调用event.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>

我怎样才能使这项工作适用于 onmouseout 而不是 onmouseover?
哎呀。我的错。我只是在上面的代码中将所有“mouseover”引用更改为“mouseout”。那应该为你做。
对于第一种方法,当鼠标移过子元素时,event.target 总是匹配父元素,所以它不起作用
第二种方法也会发生同样的事情。当鼠标进入子元素时仍然触发事件
z
z666zz666z

我让它像一个魅力一样工作:

function HideLayer(theEvent){
 var MyDiv=document.getElementById('MyDiv');
 if(MyDiv==(!theEvent?window.event:theEvent.target)){
  MyDiv.style.display='none';
 }
}

啊,MyDiv标签是这样的:

<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

这样,当 onmouseout 转到孩子、孙子等时... style.display='none' 不会被执行;但是当 onmouseout 退出 MyDiv 时,它会运行。

所以不需要停止传播,使用计时器等......

感谢您的示例,我可以从中制作此代码。

希望这可以帮助某人。

也可以这样改进:

function HideLayer(theLayer,theEvent){
 if(theLayer==(!theEvent?window.event:theEvent.target)){
  theLayer.style.display='none';
 }
}

然后是这样的 DIVs 标签:

<div onmouseout="JavaScript: HideLayer(this,event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

所以更通用,不仅针对一个 div,而且不需要在每一层上添加 id="..."


s
spaceman

如果您有权访问 mouseout 方法中附加事件的元素,则可以使用 contains() 查看 event.relatedTarget 是否为子元素。

由于 event.relatedTarget 是鼠标传入的元素,如果它不是子元素,则您已将鼠标移出该元素。

div.onmouseout = function (event) {
    if (!div.contains(event.relatedTarget)) {
        // moused out of div
    }
}

A
Alan Torrico

在 Angular 5、6 和 7 上

<div (mouseout)="onMouseOut($event)"
     (mouseenter)="onMouseEnter($event)"></div>

然后上

import {Component,Renderer2} from '@angular/core';
...
@Component({
 selector: 'app-test',
 templateUrl: './test.component.html',
 styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
 public targetElement: HTMLElement;

 constructor(private _renderer: Renderer2) {
 }

 ngOnInit(): void {
 }

 ngOnDestroy(): void {
  //Maybe reset the targetElement
 }

 public onMouseEnter(event): void {
  this.targetElement = event.target || event.srcElement;
  console.log('Mouse Enter', this.targetElement);
 }

 public onMouseOut(event): void {
  const elementRelated = event.toElement || event.relatedTarget;
  if (this.targetElement.contains(elementRelated)) {
    return;
  }
  console.log('Mouse Out');
 }
}

y
yathavan

我们可以简单地检查 e.relatedTarget 是否有子类,如果为真则返回函数。

    if ($(e.relatedTarget).hasClass("ctrl-btn")){
        return;
    }

这是为我工作的代码,我用于 html5 视频播放,暂停按钮切换悬停视频元素

element.on("mouseover mouseout", function(e) {

    if(e.type === "mouseout"){

        if ($(e.relatedTarget).hasClass("child-class")){
            return;
        }

    }

});

s
saranicole

我检查原始元素的偏移量以获取元素边界的页面坐标,然后确保仅在 mouseout 超出这些边界时触发 mouseout 操作。脏,但它的工作原理。

$(el).live('mouseout', function(event){
    while(checkPosition(this, event)){
        console.log("mouseovering including children")
    }
    console.log("moused out of the whole")
})

var checkPosition = function(el, event){
    var position = $(el).offset()
    var height = $(el).height()
    var width = $(el).width()
    if (event.pageY > position.top 
|| event.pageY < (position.top + height) 
|| event.pageX > position.left 
|| event.pageX < (position.left + width)){
    return true
}
}

M
Michael Vaganov
var elem = $('#some-id');
elem.mouseover(function () {
   // Some code here
}).mouseout(function (event) {
   var e = event.toElement || event.relatedTarget;
   if (elem.has(e).length > 0) return;

   // Some code here
});

请用你的回答者给出一些解释。
K
Karthik Palaniappan

如果您向父元素添加(或拥有)一个 CSS 类或 id,那么您可以执行以下操作:

<div id="parent">
  <div>
  </div>
</div>

JavaScript:
document.getElementById("parent").onmouseout = function(e) {
  e = e ? e : window.event //For IE
  if(e.target.id == "parent") {
    //Do your stuff
  }
}

所以只有当事件在父 div 上时才会执行。


C
C0ZEN

我只是想与您分享一些东西。
我在 ng-mouseenterng-mouseleave 活动中遇到了一些困难。

案例研究:

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

为了处理菜单上的显示/隐藏,我切换了一个类。 ng-class="{down: vm.isHover}"

为了切换 vm.isHover,我使用了 ng 鼠标事件。 ng-mouseenter="vm.isHover = true" ng-mouseleave="vm.isHover = false"

目前,一切都很好,并且按预期工作。解决方案干净简单。

传入问题:

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

问题:

我发现当我的光标在浮动导航菜单上以及元素顶部时,彼此之间存在冲突。操作面板出现了,浮动导航被隐藏了。

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

第一个想法:

我尝试使用这些:

$event.stopPropagation()

$event.stopImmediatePropagation()

我结合了很多 ng 指针事件(mousemove、mouveover、...),但没有一个能帮助我。

CSS解决方案:

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

pointer-events: none;

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

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

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

更进一步:

如您所料,此解决方案有效,但我不喜欢它。
我们无法控制我们的事件,这很糟糕。
另外,您必须有权访问 vm.isHover 范围才能实现这一点,并且这可能是不可能的或不可能的,但在某种程度上很脏。
如果有人想看,我可以做一个小提琴。

尽管如此,我没有其他解决方案...
说来话长,我不能给你一个土豆,所以请原谅我的朋友。
无论如何,pointer-events: none就是生命,所以记住它。


A
Arturo Viñas Salazar

有一种简单的方法可以让它工作。您设置相同的类名的元素和所有孩子,然后:

element.onmouseover = function(event){
 if (event.target.className == "name"){
 /*code*/
 }
}

G
Galaxy IT

对于 vanillajs,您也可以使用这种方式

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 }


m
mattpr

如果由于某种原因您不想使用 mouseentermouseleave 事件,您可以使用 mouseover/mouseout 并稍微“去抖动”。

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

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

注意!如果子元素还具有 over 和/或 out 事件的侦听器并且它们的处理程序调用 event.stopPropogation() 以停止事件,则此方法将不起作用从冒泡到我们附加处理程序的父元素。如果您控制代码,这不一定是问题,但您应该注意。

示例代码

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/