RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1572824
Accepted
eccs0103
eccs0103
Asked:2024-03-23 00:10:45 +0000 UTC2024-03-23 00:10:45 +0000 UTC 2024-03-23 00:10:45 +0000 UTC

仅执行同时发生的事件之一

  • 772

描述

我有一个特定的函数(为了清楚起见,我们称之为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提供不正确的信息。我还应该尝试哪些其他方法?

javascript
  • 3 3 个回答
  • 85 Views

3 个回答

  • Voted
  1. Arbery
    2024-03-23T05:57:21Z2024-03-23T05:57:21Z

    顶部有一个解决方案,可以从数组中关闭和创建整个事件列表。因此,就我个人而言pointerdown,无论顺序如何,它总是对我有用(作者对此有抱怨)。事实上,如果浏览器中提供了该事件的所有 2-3 个变体,那么它们都会被创建。

    我的解决方案是检查列表中的事件是否存在于对象中window,并将处理程序附加到第一个不等于 的事件undefined。优点是可以设置任意顺序并且不会创建不必要的事件。缺点之一是需要检查对象中是否存在事件window。

    重要的!!关键点是数组中处理程序的顺序,在我看来,最佳的是触摸、鼠标、指针 - 但这一点在实践中得到了澄清

    const addEventListeners = (element, types, listener) => {
      for(let i = 0; i < types.length; i++) {
         if (window[`on${types[i]}`] !== undefined) {
           element.addEventListener(types[i], e => listener(e));
           return;
        } 
      }
    };
    
    const foo = e => {
      console.log(e.type, 'event');
      result.insertAdjacentHTML('beforeend', `<div>${e.type}, event</div>`);
    }
    const element = document.querySelector('.button');
    const result = document.querySelector('.result');
    
    addEventListeners(
      element,
      ['touchstart','mousedown', 'pointerdown'],
      event => foo(event)
    );
    <button class='button'>кнопка</button>
    <div class='result'></div>

    这个想法来自于这里浏览器中事件的存在,你也可以在这里查看浏览器如何处理手机屏幕上的触摸(js触摸事件)。

    • 1
  2. Best Answer
    Stanislav Volodarskiy
    2024-03-24T03:20:13Z2024-03-24T03:20:13Z

    接收到的第一个事件将处理程序循环回自身。它停止响应其他类型的事件:

    const makeGate = f => {
        let password = undefined;
        return (key, event) => {
            if (password === key) {
                f(event);
            } else if (password === undefined) {
                password = key;
                f(event);
            }
        };
    };
    
    const addEventListeners = (element, types, listener) => {
        const gate = makeGate(listener);
        types.forEach(
            type => element.addEventListener(type, event => gate(type, event))
        );
    };
    
    ...
    
        addEventListeners(
            element,
            ['mousedown', 'pointerdown', 'touchstart'],
            event => foo()
        );
    
    • 1
  3. Stanislav Volodarskiy
    2024-03-23T04:35:28Z2024-03-23T04:35:28Z

    注意:这是一次很好的尝试,但很可能不会按预期工作。依靠一定的时间间隔,希望一切都足够快,是不可靠的。起初我认为两毫秒就足够了。但事实证明,在强大的硬件上,在空白页面上,间隔可能会更大。将其增加到十毫秒。你自己看。我会做一个幂等处理程序,尽管这需要很多工作。

    尝试十毫秒节流:

    const throttle = (f, delay) => {
        let waitFor = 0;
        return (...args) => {
            const now = new Date().getTime();
            if (now > waitFor) {
                waitFor = now + delay;
                // setTimeout(() => f(...args));
                f(...args);
            }
        };
    };
    
    const addEventListeners = (element, types, listener) => {
        const handler = throttle(listener, 10);
        types.forEach(type => element.addEventListener(type, handler));
    };
    
    ...
    
        addEventListeners(
            element,
            ['mousedown', 'pointerdown', 'touchstart'],
            event => foo()
        );
    

    PS间隔选择为十毫秒是因为:

    1. 每秒最多可以执行100次处理,由于通常的处理速率不超过每秒60次,因此用户不会注意到各种事件的遗漏。

    2. 一个物理事件发生的不同逻辑事件将以较小的时间间隔被触发。

    PPS我想写setTimeout(() => f(...args));。但随后它就会停止event.preventDefault();在体内发挥作用f,而这通常是必要的。另一方面,f它必须尽快执行,否则节流根本不起作用。f如果它执行此event.preventDefault();操作并将所有其余处理传递给调用,那将是理想的setTimeout(...);。

    另一方面,如果您需要PPPSpreventDefault,那么应该在所有处理程序中不进行限制的情况下完成。那么方案就不同了:

    const addEventListeners = (element, types, listener, preventDefault = false) => {
        const handler = throttle(listener, 10);
        let h = handler;
        if (preventDefault) {
            h = event => {
                event.preventDefault();
                handler(event);
            };
        }
        types.forEach(type => element.addEventListener(type, h));
    };
    
    ...
    
        addEventListeners(
            element,
            ['mousedown', 'pointerdown', 'touchstart'],
            event => foo(),
            preventDefault = true
        );
    

    总之,没有悲伤,女人买了一头猪……

    • 0

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5