为什么在子元素上执行 mousein 操作时会触发 mouseout 事件?

9
mouseout事件的定义(来源于MDN)如下:

mouseout事件在指针设备(通常是鼠标)移动到侦听器所附加的元素或其子级之一时触发。

因此,如果我有一个容器
,并将mouseout事件附加到它,那么如果鼠标从其任何子级移出,则期望触发该事件。但是实际上,即使鼠标移入容器的子元素中,mouseout事件也会被触发。以下是示例:

x = 0;
$(document).ready(function(){
    $("div.over").mouseout(function(){
        $(".over span").text(x += 1);
    });    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="over" style="background-color:lightgray;padding:20px;width:250px;float:left">
  <h3 style="background-color:white;">Mouseout event triggered: <span></span></h3>
</div>

当鼠标进入

时,div.over上的mouseout事件会被触发。为什么呢? 编辑:请提供权威参考来支持您的说法。


3
因为鼠标指针离开了当前元素,请使用"mouseleave"事件代替。当指针离开相关元素的边界框时,它会被触发。 - Tolgahan Albayrak
@TolgahanAlbayrak 不,鼠标没有移出当前元素。h3div.outer内部。 - user31782
@user31782 :) 假设你的头前面有一盘子,然后有只苍蝇落在了你的肩膀上。当它落下时,你会说你身上有只苍蝇。现在假设这只苍蝇跳到了那个盘子里,而你并没有看见。那么,你认为这只苍蝇还在你身上吗? - Tolgahan Albayrak
1
@TolgahanAlbayrak 但这并不是 MDN 的定义所说的。 - Mahi
1
@TolgahanAlbayrak 如果你在家里,然后进入你的房间,这是否意味着你已经离开了家? - Mahi
显示剩余6条评论
2个回答

14
由于您的div包含子元素,当您将鼠标移到子元素上时,您将“mouseout”容器,这是设计上的。由于它位于自己的可见空间之外,而位于其子元素的可见空间之内。由于子元素也在父元素内部,它“继承”了事件,因为它被视为一个单独的体积,但仍然在父元素的空间内。这就是为什么当您从子元素“mouseout”时会触发事件。这被称为“冒泡”,事件会沿着元素的家族树向上冒泡。
正如Mahi指出的,如果您使用“mouseleave”,它只会在离开附加元素的区域时触发一次。
MDN文档在这里解释了区别: https://developer.mozilla.org/en-US/docs/Web/Events/mouseleave 但权威答案最好在W3C DOM规范中查看:
引用: 它必须在指针设备从一个元素移动到其后代元素的边界时触发。
因此,它明确说明当您移动到其子元素之一时,必须触发mouseout事件。因此,这种情况发生的原因是设计上的,根据规范:

https://www.w3.org/TR/DOM-Level-3-Events/#event-type-mouseout

我已经添加了一个示例来展示区别。

x = 0;
$(document).ready(function(){
    $("div.over").mouseout(function(e){
        $(".over span").text(x += 1);
        console.log(e.target);
    });    
    $("div.over > h3").mouseover(function(){
        $(".over > h3").css("color", "red");
    }).mouseout(function(){
        $(".over > h3").css("color","black");
    });
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="over" style="background-color:lightgray;padding:20px;width:250px;float:left">
  <h3 style="background-color:white;">Mouseout event triggered: <span></span></h3>
</div>

x = 0;
$(document).ready(function(){
    $("div.over").mouseleave(function(){
        $(".over span").text(x += 1);
    });    
    $("div.over > h3").mouseover(function(){
        $(".over > h3").css("color", "red");
    }).mouseout(function(){
        $(".over > h3").css("color","black");
    });
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="over" style="background-color:lightgray;padding:20px;width:250px;float:left">
  <h3 style="background-color:white;">Mouseout event triggered: <span></span></h3>
</div>

现在,如果你将子元素在“z空间”中向下移动,它将不再影响mouseout事件。

x = 0;
$(document).ready(function(){
    $("div.over").mouseout(function(){
        $(".over").css("background","red");
    }); 
    $("div.over").mouseover(function(){
        $(".over").css("background","#444");
    });       
    $("div.over > h3").css("display", "block");
    $("div.over > h3").css("position", "relative");
    $("div.over > h3").css("z-index", -1000);
    $("div.over > h3").mouseover(function(){
        $(".over > h3").css("color", "red");
    }).mouseout(function(){
        $(".over > h3").css("color","black");
    });
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="over" style="background-color:lightgray;padding:20px;width:250px;float:left">
  <h3 style="background-color:white;">Mouseout event triggered: <span></span></h3>
</div>


“由于它超出了自己的可见空间”,沿着同样的线路,我可以说一旦我“mouseover”子项,我就会“mouseleave”容器。但为什么mouseleave没有被触发?mouseleave和mouseout的定义都使用了“off”这个词。对于它们来说,“off”必须具有相同的意义。 - user31782
@user31782 这是关于这些事件功能的问题还是关于 Web API 文档措辞的问题?mouseleave 相对于整个区域,忽略任何子元素区域,而 mouseout 仅相对于可见区域。 - Espen
此外,你第三个例子中的 JavaScript 代码是不正确的。当 div.over 上发生 mouseovermouseout 事件时,你正在重写 mouseout。你的声明“现在,如果你将子元素向下移动到“z轴”中,它就不再影响mouseout事件了”是错误的。请参考:https://jsfiddle.net/。 - user31782
在你的代码示例中,通过将容器设置为相对定位,你破坏了z-index部分。这个代码示例表明只有当鼠标从父元素移除时才会触发事件: https://jsfiddle.net/6ey8sh3v/3/ 我认为MDN文档很清楚地解释了它的工作原理。mouseout和mouseover事件沿嵌套元素层次结构向上冒泡,每个子元素都会触发一次,而mouseleave和mouseenter则不会。文档具体说明了何时使用哪种事件。 - Espen
1
@user31782 我已更新了我的回答,并提供了一份我认为是来自W3C规范的权威答案链接。 - Espen
显示剩余2条评论

5
mouseout事件在鼠标指针离开选定元素及其子元素时触发。 mouseleave事件仅在鼠标指针离开选定元素时触发。

1
谢谢您的回复,但我并不是在问mouseleave。我在问:当鼠标进入子元素(h3)时,为什么会触发父元素(div.over)上的mouseout事件?因为鼠标仍然在容器div内,并没有移出任何元素。 - user31782
我看过的最好的问题。如果你有答案,请也告诉我一声 :) 否则我们将更改 MDN 的定义。 - Mahi
如果我得到答案,我会在这篇文章中通知你。顺便问一下,除了 MDN 之外,你知道其他权威的 JavaScript 参考资料吗? - user31782

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接