reactjs - 如何在 React 应用程序中的所有 onclick 处理程序之前运行通用代码? (解决 Safari 中键点击错误)

标签 reactjs mouseevent dom-events stoppropagation

如何在所有 onclick 之前运行代码React 应用程序中的处理程序而无需向每个处理程序添加代码?具体来说,我想全局确保所有 React onclick 都忽略中键点击。处理程序。目标是围绕 12-year-old WebKit bug 进行工作其中 Safari 发出 click按下鼠标中键时的事件,而不是 auxclick 事件是 mandated由 Chrome 和 Firefox 发布的 W3C 标准。

由于某些用户在使用鼠标滚轮滚动时意外触发中键单击,因此我想全局忽略这些意外单击。怎么办?

我要注入(inject)的代码非常简单:

if (e.button !== 0) {
  e.stopPropagation();
}

但我不确定在哪里注入(inject)它,以便它在我的应用程序中的所有事件处理程序之前运行。

一个潜在的复杂性是我不想完全忽略中键点击(因为浏览器有一个默认行为,即中键点击 <a> 元素将在新选项卡中打开链接)。相反,我只是想阻止 React 对那些无效的 click 做任何事情。事件。

最佳答案

为了解决这个问题,我认为我必须做一些棘手的事情,比如猴子修补 React,但事实证明,一个不棘手的解决方案是可能的:只需将整个应用程序包装在捕获点击事件的顶级组件中使用onClickCapture事件而不是普通的点击事件。这是我为此目的编写的一个简单组件。

忽略SafariMiddleClicks.tsx

import React, { useCallback, MouseEventHandler, ReactNode } from 'react';
export default function IgnoreSafariMiddleClicks({ children }: { children: ReactNode }) {
  const onClick = useCallback<MouseEventHandler>(e => {
    if (e.button !== 0) {
      // Prevent middle clicks from being handled by click handlers on Safari
      // browsers, in order to work around this 12-year-old WebKit bug:
      // https://bugs.webkit.org/show_bug.cgi?id=22382
      e.stopPropagation();
    }
  }, []);
  return <div onClickCapture={onClick}>{children}</div>;
}

如果您不使用 TypeScript,这里是该组件的纯 JS 版本:

忽略SafariMiddleClicks.js

import React from 'react';
export default function IgnoreSafariMiddleClicks({ children }) {
  const onClick = useCallback(e => {
    if (e.button !== 0) {
      // Prevent middle clicks from being handled by click handlers on Safari
      // browsers, in order to work around this 12-year-old WebKit bug:
      // https://bugs.webkit.org/show_bug.cgi?id=22382
      e.stopPropagation();
    }
  }, []);
  return <div onClickCapture={onClick}>{children}</div>;
}

用法

import React from 'react';
import IgnoreSafariMiddleClicks from './IgnoreSafariMiddleClicks';
export default function App() {
  return (
    <IgnoreSafariMiddleClicks>
      <div>
        <button onClick={() => console.log('Left clicked!')}>
          click me!
        </button>
      </div>
    </IgnoreSafariMiddleClicks>
  );
}

我发现的一个问题是SyntheticEvent.nativeEvent.stopImmediatePropagation在这种情况下不起作用,因为之后会继续调用其他 React 事件处理程序。我必须使用 SyntheticEventstopPropagation 方法.

我花了一段时间才找到这个解决方案(尤其是捕获阶段技巧和 stopPropagationstopImmediatePropagation 问题),但我没有看到这个吞中按钮的解决方案可以在网上的任何其他地方找到,因此将其发布在这里以帮助下一个人搜索解决方案。

另一种解决方案可能是添加一个polyfill,用符合标准的auxclick事件替换Safari的不良click事件,但Google didn't return anything promising编写事件填充超出了我对 React 事件处理的有限知识,因此我选择了上面的包装组件解决方案。

关于reactjs - 如何在 React 应用程序中的所有 onclick 处理程序之前运行通用代码? (解决 Safari 中键点击错误),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59556495/

相关文章:

javascript - 浏览器调试 : how to see what Javascript functions was called after some action?

javascript - 如何检查是否按下了某个键?

reactjs - 查询时遇到子选择,但存储没有对象引用 Apollo 链接状态

javascript - React this.state 误解

javascript - 限制 map 后结果

java - 识别 "click-point"和 JfreeChart 图中单击的子图时出现问题

javascript - Meteor + React 中的错误消息 "state is not defined"。

java - 为什么按一次鼠标会调用 Java MousePressed 两次?

cocoa - 如何点击 cocoa 网页 View 的透明部分

javascript - 将 DOM 事件添加到 Angular 指令中