因为MouseEvent
或WheelEvent
类型上不存在简单的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
有点大(
如果有人知道我该如何解决这个问题,那就太好了,但问题主要是了解是什么造成了差异。
规范定义了此行为。在调度事件的步骤结束时,我们有:
- (循环遍历祖先元素,将它们添加到事件的路径中(
。
- 将事件的路径设置为空列表。
也就是说,在步骤 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>
我有点不确定为什么会发生这种情况。可能是他们在后台一遍又一遍地重用相同的事件作为优化,以节省内存使用量。减少他们需要分配和释放内存的次数。这可能最终成为低端设备性能的一个重要因素。