JavaScript与jQuery中mouseenter/mouseleave与mouseover/mouseout冒泡机制解析
本文深入解析JavaScript中鼠标事件冒泡机制,重点区分mouseenter/mouseleave与mouseover/mouseout的行为差异。通过原生JS案例展示冒泡导致的意外触发问题,并介绍利用relatedTarget和contains方法过滤事件的方案。同时解析jQuery内部如何通过代理机制统一处理这些事件,确保开发者能精准控制鼠标交互逻辑。
mouseenter与mouseleave事件解析
首先介绍基础的鼠标移动交互场景。
当鼠标指针移入元素区域时触发的事件。
具体实现代码示例如下。
mouseenter 事件不会向父级冒泡。
mouseover 事件会向父级冒泡。
对于 mouseover,只要鼠标指针经过被选元素本身或其任意子元素,都会触发该事件;而 mouseenter 仅在鼠标指针直接穿过被选元素本身时才会触发,经过子元素不会触发。
当鼠标指针移出元素区域时触发的事件。
具体实现代码示例如下。
mouseleave 事件不会向父级冒泡。
mouseout 事件会向父级冒泡。
对于 mouseout,只要鼠标指针离开被选元素或其任意子元素,都会触发该事件;而 mouseleave 仅在鼠标指针直接离开被选元素本身时才会触发,从子元素移向父元素不会触发。
mouseout冒泡问题与解决方案
通过一个实际案例来观察上述机制带来的影响。
若对嵌套层级的元素绑定 mouseout 事件,会发现其行为与直觉预期不符。
具体代码实现如下。
<!DOCTYPE html><div class="out overout" style="width:40%;height:120px; margin:0 15px;background-color:#D6EDFC;float:left;" data-mce-style="width: 40%; height: 120px; margin: 0 15px; background-color: #d6edfc; float: left;"><p style="border:1px solid red" data-mce-style="border: 1px solid red;">外部子元素</p><div class="in overout" style="width:60%;background-color:#FFCC00;margin:10px auto;" data-mce-style="width: 40%; height: 120px; margin: 0 15px; background-color: #d6edfc; float: left;"><p style="border:1px solid red" data-mce-style="border: 1px solid red;">内部子元素</p><p id="inshow">0</p>
</div><p id="outshow">0</p>
</div><script type="text/javascript">
var i = 0;
var k = 0;
document.querySelectorAll('.out')[0].addEventListener('mouseout',function(e){
document.querySelectorAll("#inshow")[0].textContent = (++i)
e.stopPropagation();
},false)
document.querySelectorAll('.in')[0].addEventListener('mouseout',function(){
document.querySelectorAll("#outshow")[0].textContent = (++k)
},false)
</script>
通过案例观察发现 mouseout 事件存在以下问题。
1. 即使调用 stopPropagation 也无法完全阻止冒泡带来的逻辑干扰。 2. 鼠标在内部子元素上移动时,父级元素的 mouseout 也会被频繁触发。
mouseover 事件同样存在此问题。在 stopPropagation 方法失效或无法解决根本逻辑的情况下,我们需要通过其他方式来停止不必要的冒泡触发。
1. 为了阻止 mouseover 和 mouseout 的反复触发,这里需要用到 event 对象的一个属性 relatedTarget,这个属性就是用来判断 mouseover 和 mouseout 事件目标节点的相关节点的属性。简单来说,当触发 mouseover 事件时,relatedTarget 属性代表的是鼠标刚刚离开的那个节点;当触发 mouseout 事件时,它代表的是鼠标移向的那个对象。由于 MSIE 不支持这个属性,不过它有代替的属性,分别是 fromElement 和 toElement。 2. 有了这个属性,我们就能够清楚地知道我们的鼠标是从哪个对象移过来,又是要移动到哪里去了。这样我们就能够通过判断这个相关联的对象是否在我们要触发事件的对象的内部,或者是不是就是这个对象本身。通过这个判断我们就能够合理的选择是否真的要触发事件。 3. 这里我们还用到了一个用于检查一个对象是否包含在另外一个对象中的方法,contains 方法。MSIE 和 FireFox 分别提供了检查的方法,这里封装了一个函数。
jQuery内部处理机制
jQuery 对这类事件的处理逻辑与原生方案类似。
具体实现代码示例如下。
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout",
pointerenter: "pointerover",
pointerleave: "pointerout"
}, function(orig, fix) {
jQuery.event.special[orig] = {
delegateType: fix,
bindType: fix,
handle: function(event) {
var ret,
target = this,
related = event.relatedTarget,
handleObj = event.handleObj;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !jQuery.contains(target, related))) {
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = fix;
}
return ret;
}
};
});anime.js 快速入门:怎么引入库并编写第一个 DOM 动画
2026-11-05 14:22:43
在线JS解密后变量名仍乱码?结合浏览器DevTools调试还原
2026-11-04 20:26:01
怎么在浏览器中运行JavaScript代码并查看输出结果
2026-11-04 09:07:33
JS 获取屏幕分辨率:window.screen.width 与 height 用法
2026-11-04 08:52:48
为什么推荐用DOMContentLoaded代替window.onload初始化JS
2026-11-04 08:42:33
