【问题标题】:Prevent onmouseout when hovering child element of the parent absolute div WITHOUT jQuery在没有jQuery的情况下悬停父绝对div的子元素时防止onmouseout
【发布时间】: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

标签: javascript css dom-events onmouseout


【解决方案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, mouseentermouseleave 是原生 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>

                              【讨论】:

                                猜你喜欢
                                • 2019-05-23
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2011-07-18
                                • 2011-10-10
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多