为什么事件上的组合路径在延迟时返回不同的值?



因为MouseEventWheelEvent类型上不存在简单的path,所以我正在使用另一种方法:composedPath方法。直到现在,我才体验到它们之间的区别。我用requestAnimationFrame包裹了我的wheel侦听器,令人惊讶的是composedPath返回了一个空数组。举个例子:

window.addEventListener('click', e => {
console.log('at event:', Array.from(e.composedPath()));
setTimeout(() => {
console.log('after event:', Array.from(e.composedPath()));
}, 0)
})
#test {
width: 120vw;
height: 120vh;
}
<div id='test'></div>

(建议检查浏览器的控制台,因为滚动浏览window有点大(

如果有人知道我该如何解决这个问题,那就太好了,但问题主要是了解是什么造成了差异。

规范定义了此行为。在调度事件的步骤结束时,我们有:

  1. (循环遍历祖先元素,将它们添加到事件的路径中(

  1. 事件的路径设置为空列表。

也就是说,在步骤 5 中,浏览器填写路径信息,而在步骤 8(完成所有事件处理之后(,规范明确表示浏览器必须清除路径信息。

该规范通常没有说明原因,但 Kaiido 指出,添加步骤 8 的提交主要涉及与影子 DOM 相关的事件路径的处理。在任何情况下,您返回的路径部分取决于您调用composedPath的事件的currentTarget(特别是与影子 DOM 相关(,并且仅当事件被主动调度时才currentTarget有意义(一旦事件被完全调度,currentTarget设置为null(。即使步骤 8 未清除路径,如果我正确阅读了composedPath的步骤,它也只会在null时调用currentTarget时返回路径[null],这没有任何用处。(出于非 Shadow-DOM 的原因,这也是有意义的:事件对象被永远不会调用composedPath的函数关闭是很常见的,因此不必要地将这些信息保留在内存中是有意义的。路径中后来从 DOM 中删除的任何元素都会无缘无故地弄乱内存。但有证据表明,这不是添加步骤8的主要动机。

如果您需要事件的路径,只需在处理期间而不是之后获取它。

window.addEventListener("click", (e) => {
const path = Array.from(e.composedPath(), (e) => e === window ? "window" : e.nodeName);
setTimeout(() => {
console.log(path);
}, 0);
});

window.addEventListener("click", (e) => {
const path = Array.from(e.composedPath(), (e) => e === window ? "window" : e.nodeName);
setTimeout(() => {
console.log(path);
}, 0);
});
#test {
width: 120vw;
height: 120vh;
}
<div id='test'></div>

您可以通过在事件触发时计算一次composedPath来解决问题,并将其存储在变量中以便在超时内重用。

window.addEventListener('click', e => {
const a = e.composedPath();
console.log('at event:', Array.from(a).length);
setTimeout(() => {
console.log('after event:', Array.from(a).length);
}, 0)
})
#test {
width: 120vw;
height: 120vh;
}
<div id='test'></div>

我有点不确定为什么会发生这种情况。可能是他们在后台一遍又一遍地重用相同的事件作为优化,以节省内存使用量。减少他们需要分配和释放内存的次数。这可能最终成为低端设备性能的一个重要因素。

最新更新