如何在 React 应用程序中的所有 onclick 处理程序之前 运行 公共代码? (解决 Safari 中键点击错误)
How to run common code before all onclick handlers in a React app? (to work around Safari middle-button click bug)
如何在 React 应用程序中的所有 onclick
处理程序之前 运行 编写代码,而不必向每个处理程序添加代码?具体来说,我想在全局范围内确保所有 React onclick
处理程序都忽略中键点击。目标是根据 W3C 标准解决 12-year-old WebKit bug where Safari emits a click
event when the middle mouse button is pressed, instead of the auxclick
event that's mandated 问题,该标准由 Chrome 和 Firefox 发布。
因为一些用户在使用鼠标滚轮滚动时不小心触发了中间按钮点击,我想全局忽略这些意外点击。怎么样?
我要注入的代码很简单:
if (e.button !== 0) {
e.stopPropagation();
}
但我不确定将它注入到哪里,以便它 运行 在我的应用程序中的所有事件处理程序之前。
一个潜在的并发症是我不想完全忽略中间点击(因为浏览器有一个默认行为,中间点击 <a>
元素将在新选项卡中打开 link ).相反,我只是想阻止 React 对那些无效的 click
事件做任何事情。
为了解决这个问题,我想我必须做一些棘手的事情,比如猴子修补 React,但事实证明,一个不棘手的解决方案是可能的:只需将整个应用程序包装在一个顶级组件中,该组件捕获单击事件使用 onClickCapture
事件而不是正常的 click
事件。这是我为此目的编写的一个简单组件。
IgnoreSafariMiddleClicks.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 版本:
IgnoreSafariMiddleClicks.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
doesn't work in this scenario, because other React event handlers continue to be called afterwards. I had to use the stopPropagation
method of SyntheticEvent
。
我花了一段时间才想出这个解决方案(尤其是捕获阶段的技巧和 stopPropagation
与 stopImmediatePropagation
的问题),我没有看到这个中间按钮- 在网上其他任何地方吞下解决方案,因此将其张贴在这里以帮助下一个寻找解决方案的人。
另一种解决方案可能是添加一个 polyfill,用符合标准的 auxclick
事件替换 Safari 的不良 click
事件,但是 Google didn't return anything promising 并编写一个事件polyfill 超出了我对 React 事件处理的有限了解,所以我选择了上面的 wrapper-component 解决方案。
如何在 React 应用程序中的所有 onclick
处理程序之前 运行 编写代码,而不必向每个处理程序添加代码?具体来说,我想在全局范围内确保所有 React onclick
处理程序都忽略中键点击。目标是根据 W3C 标准解决 12-year-old WebKit bug where Safari emits a click
event when the middle mouse button is pressed, instead of the auxclick
event that's mandated 问题,该标准由 Chrome 和 Firefox 发布。
因为一些用户在使用鼠标滚轮滚动时不小心触发了中间按钮点击,我想全局忽略这些意外点击。怎么样?
我要注入的代码很简单:
if (e.button !== 0) {
e.stopPropagation();
}
但我不确定将它注入到哪里,以便它 运行 在我的应用程序中的所有事件处理程序之前。
一个潜在的并发症是我不想完全忽略中间点击(因为浏览器有一个默认行为,中间点击 <a>
元素将在新选项卡中打开 link ).相反,我只是想阻止 React 对那些无效的 click
事件做任何事情。
为了解决这个问题,我想我必须做一些棘手的事情,比如猴子修补 React,但事实证明,一个不棘手的解决方案是可能的:只需将整个应用程序包装在一个顶级组件中,该组件捕获单击事件使用 onClickCapture
事件而不是正常的 click
事件。这是我为此目的编写的一个简单组件。
IgnoreSafariMiddleClicks.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 版本:
IgnoreSafariMiddleClicks.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
doesn't work in this scenario, because other React event handlers continue to be called afterwards. I had to use the stopPropagation
method of SyntheticEvent
。
我花了一段时间才想出这个解决方案(尤其是捕获阶段的技巧和 stopPropagation
与 stopImmediatePropagation
的问题),我没有看到这个中间按钮- 在网上其他任何地方吞下解决方案,因此将其张贴在这里以帮助下一个寻找解决方案的人。
另一种解决方案可能是添加一个 polyfill,用符合标准的 auxclick
事件替换 Safari 的不良 click
事件,但是 Google didn't return anything promising 并编写一个事件polyfill 超出了我对 React 事件处理的有限了解,所以我选择了上面的 wrapper-component 解决方案。