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


169

onmouseout在绝对位置div中的函数遇到麻烦。当鼠标单击div中的一个子元素时,将触发mouseout事件,但我不希望它触发,直到鼠标不在父绝对div之外。

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

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

我在这里找到了类似的帖子:如何禁用由子元素触发的mouseout事件?

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


我最终使用超时解决了问题,并在子元素悬停时清除了该问题
John

嗨,我看过您的演示,但是当我浏览右下面板中的元素时,什么都没有发生?
约翰

1
如果您希望在将鼠标悬停在子元素上时防止在父事件上发生悬停,请找到以下答案:javascript mouseover / mouseout
user823527'8

Answers:


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

还有一个新的JSFiddleEDIT更新了链接


1
您应该在小提琴中添加mouseout事件,以便可以对其进行测试。警报或应采取的措施。
约翰

抱歉,您好像有alert('MouseOut'),但是它对我不起作用?我已经打运行,没有JS库选项
约翰·

在Safari和Chrome上进行了测试,但在所有功能上都可以使用!您的浏览器是什么?
2011年

2
如果您有大孩子怎么办?或曾孙?也就是说,您可以有一个递归的解决方案,而不仅仅是卑鄙的孩子吗?
Roozbeh15年

19
这行不通。mouseleave是正确的答案
代码2014年

126

使用onmouseleave

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

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

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

或者,在jQuery中:

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

一个例子在这里


3
onMouseLeave我最终也使用了它,因为它不会冒泡。
Alecz

节省了我很多时间。thnx
npo

mouseleave不能取消。
grecdev

@grecdev,说更多!
本·惠勒

96

对于在某些情况下(IE11或更高版本)可以使用的更简单的纯CSS解决方案,可以通过将子级pointer-events设置为来删除子级none

.parent * {
     pointer-events: none;
}

4
辉煌!完全解决了我的问题!
Anna_MediaGirl 2014年

4
这应该是第一个或至少第二个答案,我已经准备好应对事件侦听器的麻烦,这也完全解决了我的问题。很简单 !
Mickael V. 2014年

9
这样可以防止mouseout被触发,但是对我来说,这也可以防止单击实际的孩子作为菜单中的一个选择,这是菜单的重点。
johnbakers

@hellofunk这就是为什么要将click事件应用于父级的原因。所需的确切代码因实现而异,此答案仅表明使用该pointer-events属性
Zach Saucier 2015年

1
它仅适用,如果你没有在区域内其他控制器单元(编辑,删除),因为该解决方案块他们太..
佐尔坦SULE

28

这是一个基于以下内容的更优雅的解决方案。它说明了来自多个级别儿童的事件激增。它还解决了跨浏览器的问题。

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浏览器中,它的工作原理就像一种魅力。谢谢。

1
当我将上述函数粘贴到脚本中时,Dreamweaver和FF在第一行“函数onMouseOut(this,event){”上引发错误。看来您不允许将“ this”作为参数。当我从输入参数中省略“ this”时,我猜想这是可行的,但是我不确定,因为“我不知道我不知道的东西”。
塔库斯

1
我在Chrome的同一行中也弹出了相同的问题,this但从功能定义行中删除了@GregoryLewis提到的已解决问题。同样,为了获得更多的跨浏览器支持,请尝试以下操作:if(window.addEventListener){document.getElementById('parent')。addEventListener('mouseout',onMouseOut,true); }否则,如果(window.attachEvent){document.getElementById('parent')。attachEvent(“ onmouseout”,onMouseOut); }
DWils

很英俊!如果需要支持旧版浏览器的最佳答案。
BurninLeo

13

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

13

而不是onmouseout使用onmouseleave。

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

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

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

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

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

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


这是一个很大的呼喊,如果您有类似情况,值得一试
克里斯(Chris

12

如果您使用的是jQuery,则还可以使用“ mouseleave”函数,该函数可以为您处理所有这一切。

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

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


8

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

看看他们是如何做到这一点,如果你想,做你自己
你可以创建自定义只用事件部分(及其附属)Mootools的的“精益平均”版本。


7

尝试 mouseleave()

范例:

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

   </div>
</div>

;)


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>

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

希望能帮助到你 :)



2

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

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”。那应该为你做。
selbie 2011年

对于第一种方法,当鼠标悬停在子元素上时,event.target始终与父元素匹配,因此它不起作用
John

第二种方法也会发生相同的情况。当鼠标进入子元素时,它仍会触发事件
John

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

然后像这样的DIVs标签:

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

因此,更通用的是,不仅适用于一个div,而且无需id="..."在每个图层上添加。


1

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

至于event.relatedTarget是哪个鼠标已经传递到,如果它不是一个子元素的元素,你的元素打探出。

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

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

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


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上时才执行内容。


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.