描述
我有一个特定的函数(为了清楚起见,我们称之为foo
),如果用户将光标移动到某个元素上,应该执行该函数。由于我不知道用户拥有什么设备,因此我附加了所有可能的处理程序:
element.addEventListener(`mousedown`, (event) => {
foo();
});
element.addEventListener(`pointerdown`, (event) => {
foo();
});
element.addEventListener(`touchstart`, (event) => {
foo();
});
现在我需要确保当其中一个处理程序同时开始工作时,其余处理程序保持沉默,以便该函数执行 1 次,而不是 3 次。我这样做:
const controller = new AbortController();
await new Promise((resolve) => {
element.addEventListener(`mousedown`, (event) => {
resolve();
}, { signal: controller.signal });
element.addEventListener(`pointerdown`, (event) => {
resolve();
}, { signal: controller.signal });
element.addEventListener(`touchstart`, (event) => {
resolve();
}, { signal: controller.signal });
});
foo();
controller.abort();
它运作良好,但只能运作一次。为了让它再次工作,我需要创建一个函数,每次完成后都会生成相同的结构。
问题
好吧,解决方案就是解决方案的工作原理。但如果我们考虑到除了mousedown
、之外pointerdown
,touchstart
我们还添加mousemove
、pointermove
和touchmove
一堆其他类似的处理程序,那么在我看来,它看起来很乏味。
问题
有替代的、更好的解决方案吗?
或者这个设计可以简化吗?
常问问题
— 为什么不只使用
pointer...
监听器?它们也是通用的。- 它们有时无法正常工作。例如,pointermove
在移动设备上使用时,放下鼠标后会冻结 24-30 帧。
— 为什么不为每个设备使用正确的处理程序?- 我很乐意。但如何才能 100% 知道处理程序是否“正确”呢?处理程序无处不在,但不做出反应。
navigator.userAgent
提供不正确的信息。我还应该尝试哪些其他方法?
顶部有一个解决方案,可以从数组中关闭和创建整个事件列表。因此,就我个人而言
pointerdown
,无论顺序如何,它总是对我有用(作者对此有抱怨)。事实上,如果浏览器中提供了该事件的所有 2-3 个变体,那么它们都会被创建。我的解决方案是检查列表中的事件是否存在于对象中
window
,并将处理程序附加到第一个不等于 的事件undefined
。优点是可以设置任意顺序并且不会创建不必要的事件。缺点之一是需要检查对象中是否存在事件window
。重要的!!关键点是数组中处理程序的顺序,在我看来,最佳的是触摸、鼠标、指针 - 但这一点在实践中得到了澄清
这个想法来自于这里浏览器中事件的存在,你也可以在这里查看浏览器如何处理手机屏幕上的触摸(js触摸事件)。
接收到的第一个事件将处理程序循环回自身。它停止响应其他类型的事件:
注意:这是一次很好的尝试,但很可能不会按预期工作。依靠一定的时间间隔,希望一切都足够快,是不可靠的。起初我认为两毫秒就足够了。但事实证明,在强大的硬件上,在空白页面上,间隔可能会更大。将其增加到十毫秒。你自己看。我会做一个幂等处理程序,尽管这需要很多工作。
尝试十毫秒节流:
PS间隔选择为十毫秒是因为:
每秒最多可以执行100次处理,由于通常的处理速率不超过每秒60次,因此用户不会注意到各种事件的遗漏。
一个物理事件发生的不同逻辑事件将以较小的时间间隔被触发。
PPS我想写
setTimeout(() => f(...args));
。但随后它就会停止event.preventDefault();
在体内发挥作用f
,而这通常是必要的。另一方面,f
它必须尽快执行,否则节流根本不起作用。f
如果它执行此event.preventDefault();
操作并将所有其余处理传递给调用,那将是理想的setTimeout(...);
。另一方面,如果您需要PPPS
preventDefault
,那么应该在所有处理程序中不进行限制的情况下完成。那么方案就不同了:总之,没有悲伤,女人买了一头猪……