老问题,但在 imo 洞察力中仍然没有很好的最新答案。
由于 jQuery 对事件和处理程序使用 Javascript 措辞,但对这些事件和处理程序有自己的未记录但不同的解释,让我首先阐明与纯 Javascript 观点的区别:
- 两个事件对
- 当移动速度快于浏览器对其位置的采样速度时,鼠标可以从外部/外部元素“跳转”到内部/最内部元素
- 任何
enter/over 都会获得相应的leave/out(可能迟到/跳跃)
- 事件转到指针下方的可见元素(不可见元素不能成为目标)
-
mouseenter/mouseleave
-
不会冒泡(事件对委托处理程序无用)
- 事件注册本身定义了观察和抽象的区域
- 在目标区域工作,就像一个带池塘的公园:池塘被认为是公园的一部分
- 每当元素本身或任何直接的后代第一次进入/离开时,都会在目标/区域上发出事件
- 进入后代、从一个后代移动到另一个后代或返回目标不会完成/重新启动
mouseenter/mouseleave 循环(即没有触发事件)
- 如果您想使用一个处理程序观察多个区域,请在每个区域/元素上注册它或使用接下来讨论的另一个事件对
- 注册区域/元素的后代可以有自己的处理程序,创建一个独立的观察区域,具有独立的
mouseenter/mouseleave 事件周期
- 如果你想一下
mouseenter/mouseleave 的冒泡版本会是什么样子,你最终会得到类似mouseover/mouseout 的东西
-
mouseover/mouseout
- 事件气泡
- 只要指针下方的元素发生变化,就会触发事件
-
mouseout 在先前采样的元素上
- 后跟
mouseover 在新元素上
- 事件不会“嵌套”:例如之前孩子“过度”了,父母会“出局”
-
target/relatedTarget 表示新元素和上一个元素
- 如果你想看不同的区域
- 在一个共同的父级(或多个父级,它们一起涵盖您要观看的所有元素)上注册一个处理程序
- 在处理程序元素和目标元素之间寻找您感兴趣的元素;也许
$(event.target).closest(...) 适合您的需求
不那么平凡的mouseover/mouseout 示例:
$('.side-menu, .top-widget')
.on('mouseover mouseout', event => {
const target = event.type === 'mouseover' ? event.target : event.relatedTarget;
const thing = $(target).closest('[data-thing]').attr('data-thing') || 'default';
// do something with `thing`
});
如今,所有浏览器都原生支持mouseover/mouseout 和mouseenter/mouseleave。尽管如此,jQuery 不会将您的处理程序注册到 mouseenter/mouseleave,而是将它们默默地放在 mouseover/mouseout 周围的包装器上,如下面的代码所示。
模拟是不必要的、不完美的并且浪费 CPU 周期:它过滤掉了 mouseenter/mouseleave 无法获得的 mouseover/mouseout 事件,但 target 却是一团糟。真正的 mouseenter/mouseleave 会将处理程序元素作为目标,模拟可能指示该元素的子元素,即 mouseover/mouseout 携带的任何内容。
出于这个原因,我不对这些事件使用 jQuery,但是例如:
$el[0].addEventListener('mouseover', e => ...);
const list = document.getElementById('log');
const outer = document.getElementById('outer');
const $outer = $(outer);
function log(tag, event) {
const li = list.insertBefore(document.createElement('li'), list.firstChild);
// only jQuery handlers have originalEvent
const e = event.originalEvent || event;
li.append(`${tag} got ${e.type} on ${e.target.id}`);
}
outer.addEventListener('mouseenter', log.bind(null, 'JSmouseenter'));
$outer.on('mouseenter', log.bind(null, '$mouseenter'));
div {
margin: 20px;
border: solid black 2px;
}
#inner {
min-height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div id=outer>
<ul id=log>
</ul>
</div>
</body>
注意:对于委托处理程序,永远不要使用 jQuery 的“带有选择器注册的委托处理程序”。 (原因在another answer。)使用这个(或类似的):
$(parent).on("mouseover", e => {
if ($(e.target).closest('.gold').length) {...};
});
而不是
$(parent).on("mouseover", '.gold', e => {...});