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

Posted

技术标签:

【中文标题】在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout【英文标题】:Prevent onmouseout when hovering child element of the parent absolute div WITHOUT jQuery 【发布时间】:2011-06-09 12:36:14 【问题描述】:

我在使用绝对定位 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 【参考方案1】:

使用onmouseleave

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

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

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

或者,在 jQuery 中:

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

一个例子是here。

【讨论】:

onMouseLeave 也是我最终使用的,因为它不会冒泡。 为我节省了大量时间。谢谢 mouseleave 无法取消。 @grecdev,说更多!【参考方案2】:

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

.parent * 
     pointer-events: none;

浏览器支持:IE11+

【讨论】:

太棒了!完全解决了我的问题! 这应该是第一个或至少是第二个答案,我已经准备好接受事件监听器的麻烦,这也完全解决了我的问题。这么简单! 这可以防止mouseout触发,但对我来说,它也可以防止实际的孩子被点击作为菜单中的选择,这是菜单的点。 @hellofunk 这就是为什么您要将点击事件应用于父级的原因。所需的确切代码因实现而异,此答案仅建议使用 pointer-events 属性 只有在区域内没有其他控制器元素(编辑、删除)时才有效,因为此解决方案也会阻止它们..【参考方案3】:
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 有嵌套的子级,那么您必须以某种方式遍历父级元素以查找“原始元素”

EDIT 嵌套子级示例

编辑已修复,希望跨浏览器

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编辑更新链接

【讨论】:

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

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

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); 很帅!如果需要支持旧版浏览器的最佳答案。【参考方案5】:

使用 onmouseleave 代替 onmouseout。

您尚未向我们展示您的具体代码,因此我无法在您的具体示例中向您展示如何操作。

不过很简单:把onmouseout换成onmouseleave就行了。

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

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

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

平安无事 :) 尽情享受吧 :)

【讨论】:

这是一个伟大的呼喊,如果你有类似的情况,非常值得一试【参考方案6】:

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

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

do_something 会在鼠标进入targetdiv 或其任何子元素时触发,do_something_else 只会在鼠标离开targetdiv 及其任何子元素时触发。

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

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

看看他们是怎么做的,如果你愿意,可以自己做你可以create your custom Mootools 的“精益平均”版本,只有事件部分(及其依赖项)。

【讨论】:

【参考方案9】:

试试mouseleave()

例子:

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

   </div>
</div>

;)

【讨论】:

【参考方案10】:

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

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

希望对你有帮助:)

【讨论】:

【参考方案11】:

虽然你提到的solution使用jquery, mouseenter 和 mouseleave 是原生 dom 事件,因此您可以不使用 jquery。

【讨论】:

【参考方案12】:

有两种方法可以解决这个问题。

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总是匹配父元素,所以不起作用 第二种方法也会发生同样的事情。当鼠标进入子元素时仍然触发事件【参考方案13】:

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

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="..."

【讨论】:

【参考方案14】:

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

由于event.relatedTarget是鼠标传入的元素,如果不是子元素,则表示鼠标移出该元素。

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

【讨论】:

【参考方案15】:

在 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');
 

【讨论】:

【参考方案16】:

我们可以简单地检查 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;
        

    

);

【讨论】:

【参考方案17】:

我检查原始元素的偏移量以获取元素边界的页面坐标,然后确保仅在 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


【讨论】:

【参考方案18】:
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
);

【讨论】:

请用你的回答者给出一些解释。【参考方案19】:

如果您向父元素添加(或拥有)一个 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 上时才会执行。

【讨论】:

【参考方案20】:

我只是想和你分享一些东西。 我在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 就是生命,所以记住它。

【讨论】:

【参考方案21】:

有一种简单的方法可以让它工作。你设置的元素和所有的子类名一样,那么:

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

【讨论】:

【参考方案22】:

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

【讨论】:

以上是关于在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout的主要内容,如果未能解决你的问题,请参考以下文章

如何使包含 div 不影响悬停在父 div 上

jquery如何获取父元素的子元素

当鼠标悬停在绝对div上时,jQuery禁用滚动

具有绝对父级的 div 的 jQuery 动画宽度

当鼠标悬停在绝对 div 上时,jQuery 禁用滚动

VueJS父鼠标悬停事件屏蔽子鼠标悬停事件