React:如何避免在刚刚消失的元素下点击?

问题描述 投票:0回答:2

我制作了一个像 Mac/Windows 顶部菜单栏一样工作的 React 组件。一切都很好,除了一件事——在触摸屏上,当用户从菜单中选择一个选项时,点击被注册在下面,这会导致一个大问题。

我已经准备了一个沙盒,其中包含最少的可重现示例来表示该问题。要重现问题,请按照以下步骤在移动设备上,因为在桌面上它工作正常:

  1. 点击绿色的Over框 - 它打印到控制台Under框已被点击。
  2. 要重复,请单击Show Over按钮。

问题似乎是点击手机有延迟,它不关心是什么触发了它。我隐藏了 Over

onPointerUp
无论如何都会在它下面的任何地方发生点击。
event.stopPropagation()
不起作用,因为树层次结构不适用于此处。

这里是组件:

import { useState, useEffect } from "react";

const App = () => {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(
        () => {
            const callback = () => setIsVisible(false);

            window.addEventListener("pointerdown", callback);

            return () => window.removeEventListener("pointerdown", callback);
        },
        []
    );

    return (
        <div>
            <button
                type="button"
                onClick={() => {
                    setIsVisible(true);
                }}
                onPointerDown={event => event.stopPropagation()}
            >
                Show Over
            </button>
            <div>
                <button
                    type="button"
                    onClick={() => console.log("Clicked under :(")}
                >
                    Under
                </button>
                {isVisible && (
                    <div>
                        <button
                            type="button"
                            onPointerDown={event => event.stopPropagation()}
                            onPointerUp={() => {
                                setIsVisible(false);
                            }}
                        >
                            Over
                        </button>
                    </div>
                )}
            </div>
        </div>
    );
};

export default App;

问题可以在带有模拟触摸的 Chrome 开发工具中重现,也可以在 Safari iOS(但并非总是)和三星移动浏览器中重现。还没有在其他浏览器中检查过。

编辑

我发现了一个不完美的解决方案,我想改进它。我捕获了一个

onClick
事件并在 200 毫秒后删除了监听器:

onPointerUp={() => {
    setIsVisible(false);

    const lockClick = event => event.stopPropagation();

    window.addEventListener("click", lockClick, true);

    window.setTimeout(
        () => window.removeEventListener("click", lockClick, true),
        200
    );
}}

之前,我延迟了

setIsVisible(false)
本身并且它也有效,因为当点击发生时 Over 框仍然会被安装。但缩小尺寸是在盒子消失之前有一个视觉延迟。

我还在寻找更好的解决方案,一个没有

setTimeout()
.

javascript reactjs onclick touch
2个回答
0
投票

似乎可以通过使用 setTimeout 函数包装 setIsVisble 来修复它,如下所示:

<button
    type="button"
    className="button button-over"
    onPointerDown={(event) => event.stopPropagation()}
    onPointerUp={() => {
        setTimeout(() => setIsVisible(false), 5)
    }}
>

我在 Android 的 Chrome 和 Safari 上试过了。


0
投票

你正在停止

pointerDown
事件的传播,但不是 onClick 事件的传播,这是“under”div 正在监听的。有很多方法可以解决这个问题,但最简单的方法是将“under”div 更改为

<button
    type="button"
    onPointerDown={() => console.log("Clicked under :(")}>
    Under
</button>

我不完全确定为什么它只在移动设备中是一个问题——也许其他人可以在这里插话,但我怀疑浏览器可能在桌面模式下以不同的方式处理指针事件,并且可能将它们与鼠标事件或其他东西捆绑在一起。

© www.soinside.com 2019 - 2024. All rights reserved.