DOM - 同时事件的时间与 setTimeout
DOM - timing of simultaneous events vs setTimeout
假设我有一个包含多个子元素的元素,并且希望在鼠标进入或离开容器时 运行 编写一些代码。如果我天真地写:
var onHover = function (el, f) {
el.addEventListener('mouseover', function () {
f(true);
});
el.addEventListener('mouseout', function () {
f(false);
});
};
然后在某些情况下我会得到所需的行为 - 取决于回调的性质 f
。但是,当鼠标在容器 内从一个子节点移动到另一个子节点时 ,f(false)
运行 紧随其后的是 f(true)
。我不希望发生这种情况 - 我只希望 f
成为 运行 当鼠标作为一个整体进入或离开容器时,而不是当用户将鼠标拖到容器内的元素。
这是我想出的解决方案:
var onHover = function (el, f) {
var previousMouseover = false;
var receivedMouseover = false;
var pushing = false;
var pushEv = function () {
if (pushing) { return; }
pushing = true;
setTimeout(function () {
pushing = false;
if (previousMouseover !== receivedMouseover) {
f(receivedMouseover);
previousMouseover = receivedMouseover;
}
});
};
el.addEventListener('mouseover', function () {
receivedMouseover = true;
pushEv();
});
el.addEventListener('mouseout', function () {
receivedMouseover = false;
pushEv();
});
};
此解决方案与第一个解决方案一样,假定并通过 mouseout
事件在 mouseover
事件之前发送的优点来工作。我还想知道 that 是否被任何 W3C 文档正式指定,但这不是这个问题的主题,即使不是这样,也很容易尽管如此,通过在 mouseover
和 mouseout
回调中设置两个单独的变量,比如 receivedMouseover
和 receivedMouseout
,编写一个功能算法,然后在内部检查这两个变量setTimeout
回调。
问题是:mouseover
和 mouseout
事件是否都需要在 之前 任何 setTimeout
注册的回调进行处理任一事件都是 运行?
使用 mouseenter
和 mouseleave
事件代替 mouseover
和 mouseout
。
由于您已将事件侦听器附加到 parent 元素,您可以将事件来源 (event.target
) 与 parent 元素(this
或 event.currentTarget
) 在你采取行动之前。您可以进行以下操作;
var onHover = function (el, f) {
el.addEventListener('mouseover', function (evt) {
this === evt.target && f(true);
});
el.addEventListener('mouseout', function (evt) {
this === evt.target && f(false);
});
};
大多数元素都冒泡,所以在某些时候这可能是完成这项工作的正确方法。
编辑: 如评论中所述,mouseover
和 mouseout
事件在某些情况下可能会出现问题,例如 parent元素没有定义填充或边距,children 覆盖所有 parent。即使他们不这样做,鼠标的速度也可能快到足以使 JS 引擎无法对 parent 元素上的鼠标进行采样。 This fact is beautifuly explained in this article.
所以,正如接受的答案中提到的,我想 mouseenter
和 mouseleave
事件是用来解决这个问题的。因此,正确的代码应该是这样的;
var onHover = function (el, f) {
el.addEventListener('mouseenter', () => f(true));
el.addEventListener('mouseleave', () => f(false));
};
编辑 2: 好吧...实际上在这种特殊情况下有一种使用 mouseover
和 mouseout
的安全方法。这是关于在 children 上使用 CSS pointer-events
属性 禁用它们为鼠标 activity.
发出事件
var container = document.getElementById('container');
container.addEventListener('mouseover', function (ev) {
console.log(container === ev.target);
});
container.addEventListener('mouseout', function (ev) {
console.log(container === ev.target);
});
#container * {
pointer-events: none
}
<div id="container">
<div>
<span>text</span>
</div>
</div>
假设我有一个包含多个子元素的元素,并且希望在鼠标进入或离开容器时 运行 编写一些代码。如果我天真地写:
var onHover = function (el, f) {
el.addEventListener('mouseover', function () {
f(true);
});
el.addEventListener('mouseout', function () {
f(false);
});
};
然后在某些情况下我会得到所需的行为 - 取决于回调的性质 f
。但是,当鼠标在容器 内从一个子节点移动到另一个子节点时 ,f(false)
运行 紧随其后的是 f(true)
。我不希望发生这种情况 - 我只希望 f
成为 运行 当鼠标作为一个整体进入或离开容器时,而不是当用户将鼠标拖到容器内的元素。
这是我想出的解决方案:
var onHover = function (el, f) {
var previousMouseover = false;
var receivedMouseover = false;
var pushing = false;
var pushEv = function () {
if (pushing) { return; }
pushing = true;
setTimeout(function () {
pushing = false;
if (previousMouseover !== receivedMouseover) {
f(receivedMouseover);
previousMouseover = receivedMouseover;
}
});
};
el.addEventListener('mouseover', function () {
receivedMouseover = true;
pushEv();
});
el.addEventListener('mouseout', function () {
receivedMouseover = false;
pushEv();
});
};
此解决方案与第一个解决方案一样,假定并通过 mouseout
事件在 mouseover
事件之前发送的优点来工作。我还想知道 that 是否被任何 W3C 文档正式指定,但这不是这个问题的主题,即使不是这样,也很容易尽管如此,通过在 mouseover
和 mouseout
回调中设置两个单独的变量,比如 receivedMouseover
和 receivedMouseout
,编写一个功能算法,然后在内部检查这两个变量setTimeout
回调。
问题是:mouseover
和 mouseout
事件是否都需要在 之前 任何 setTimeout
注册的回调进行处理任一事件都是 运行?
使用 mouseenter
和 mouseleave
事件代替 mouseover
和 mouseout
。
由于您已将事件侦听器附加到 parent 元素,您可以将事件来源 (event.target
) 与 parent 元素(this
或 event.currentTarget
) 在你采取行动之前。您可以进行以下操作;
var onHover = function (el, f) {
el.addEventListener('mouseover', function (evt) {
this === evt.target && f(true);
});
el.addEventListener('mouseout', function (evt) {
this === evt.target && f(false);
});
};
大多数元素都冒泡,所以在某些时候这可能是完成这项工作的正确方法。
编辑: 如评论中所述,mouseover
和 mouseout
事件在某些情况下可能会出现问题,例如 parent元素没有定义填充或边距,children 覆盖所有 parent。即使他们不这样做,鼠标的速度也可能快到足以使 JS 引擎无法对 parent 元素上的鼠标进行采样。 This fact is beautifuly explained in this article.
所以,正如接受的答案中提到的,我想 mouseenter
和 mouseleave
事件是用来解决这个问题的。因此,正确的代码应该是这样的;
var onHover = function (el, f) {
el.addEventListener('mouseenter', () => f(true));
el.addEventListener('mouseleave', () => f(false));
};
编辑 2: 好吧...实际上在这种特殊情况下有一种使用 mouseover
和 mouseout
的安全方法。这是关于在 children 上使用 CSS pointer-events
属性 禁用它们为鼠标 activity.
var container = document.getElementById('container');
container.addEventListener('mouseover', function (ev) {
console.log(container === ev.target);
});
container.addEventListener('mouseout', function (ev) {
console.log(container === ev.target);
});
#container * {
pointer-events: none
}
<div id="container">
<div>
<span>text</span>
</div>
</div>