当悬停父绝对div的子元素而没有jQuery时,防止onmouseout

时间:2011-01-15 03:20:08

标签: javascript css javascript-events onmouseout

我在绝对定位div中遇到onmouseout函数时遇到问题。当鼠标命中div中的子元素时,mouseout事件会触发,但我不想在鼠标超出父绝对div之前触发它。

如何阻止mouseout事件在遇到没有jquery的子元素时触发。

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

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

但是该解决方案使用jQuery。

22 个答案:

答案 0 :(得分:88)

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 FIXED链接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 :(得分:88)

使用onmouseleave

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

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

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

或者,在jQuery中:

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

一个例子是here

答案 2 :(得分:82)

对于在某些情况下(IE11 or newer)有效的简单纯CSS解决方案,可以通过将其设置为none来删除子pointer-events

.parent * {
     pointer-events: none;
}

答案 3 :(得分:27)

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

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

答案 4 :(得分:12)

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

答案 5 :(得分:12)

如果你正在使用jQuery,你也可以使用&#34; mouseleave&#34;功能,为您处理所有这些。

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

当鼠标进入thetargetdiv或其任何子节点时,do_something将触发,do_something_else只会在鼠标离开thetargetdiv及其任何子节点时触发。

答案 6 :(得分:7)

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

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

答案 7 :(得分:7)

尝试mouseleave()

示例:

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

   </div>
</div>

答案 8 :(得分:5)

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

只需使用onmouseleave =&#34; myfunc()&#34;事件比onmousout =&#34; myfunc()&#34;事件

在我的代码中它有效!!

示例:

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

希望有所帮助:)

答案 9 :(得分:1)

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

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>

答案 10 :(得分:1)

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

答案 11 :(得分:1)

虽然你提到的solution使用了jquery, mouseentermouseleave是本机dom事件,因此您可以在没有jquery的情况下使用。

答案 12 :(得分:1)

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

由于event.relatedTarget是鼠标所传入的元素,如果它不是子元素,则您已将其从元素中删除。

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

答案 13 :(得分:1)

我用它来做它的魅力:

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';
 }
}

然后DIV标签如下:

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

更一般,不仅是一个div而且不需要在每一层添加id="..."

答案 14 :(得分:0)

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

答案 15 :(得分:0)

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

答案 16 :(得分:0)

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
});

答案 17 :(得分:0)

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

使用onmouseleave代替onmouseout。

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

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

仅此而已:)所以,很简单:)

如果不确定如何做,请参见以下说明:

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

蛋糕的和平:)享受它:)

答案 19 :(得分:0)

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

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

    }

});

答案 20 :(得分:0)

有一种简单的方法可以让它发挥作用。你设置的元素和所有子元素都设置了相同的类名,然后:

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

答案 21 :(得分:0)

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