react 怎样处理父元素的 blur 事件啊??

2021-01-13 18:14:11 +08:00
 sillydaddy
最近在用 react 实现事件响应时,用到了元素的 onBlur()回调方法。
onBlur()是写在某个 react 父元素上的,这个父元素包含了多个子元素。

<div tabIndex={-1} onBlur={onBlurParent}>
<Select>...</Select>
<Input>...</Input>
</div>

想要实现的效果就是:焦点不在父元素**并且**不在子元素上时,切换父元素的显示状态。
但结果是在子元素之间点击时,就触发了 onBlurParent(),很明显不是我想要的效果。

为此查找了网上的资料(请原谅我的初学无知),了解到了“事件冒泡”和“事件捕获”这两个事件模型。所以大概知道了发生了什么:
html 原生的事件模型中,blur 和 focus 事件不支持冒泡,然后 react 实现了它们的冒泡,结果导致在子元素之间切换焦点时,blur 事件冒泡到了父元素,触发了 onBlurParent()。

我能想到比较笨的法子来解决这个问题(记录父子元素的 focus 状态,配合全局的 click 事件),不知道大家遇到这种情况是怎么处理的呢?

(另外,我看到网上有说法( https://imweb.io/topic/5b67e61df3fbd8d9125fe801 )是,“一方面从历史沿革来看,在浏览器的早期,Netscape 浏览器是使用的 capture 事件模型,而 IE 使用的是冒泡模型,后来的标准里面就有了这两种模型可选”,——感觉不太相信,仅仅是为了兼容? 有没有比较好的这方面相关的资料呢? )
3078 次点击
所在节点    React
7 条回复
xiaoming1992
2021-01-13 19:27:51 +08:00
preventDefault ?
xiaoming1992
2021-01-13 19:31:14 +08:00
不过在`react 17`中改用了浏览器的`focusin`, `focusout`
xiaoming1992
2021-01-13 19:31:22 +08:00
hjylxmhzq
2021-01-13 22:41:55 +08:00
这种我一般用两个方法,一个是你说的全局监听 click,另一个是在 onBlur 中给一个 setTimeout,然后在同一个节点的 onFocus 中取消掉计时器
baxtergu
2021-01-14 11:47:10 +08:00
CodeSandBox: https://codesandbox.io/s/onblur-bubble-preventiton-ngsdj?file=/src/App.js

核心思路是利用 event.relatedTarget 这个属性来判断失焦是来自内部还是外部。代码如下:

import React, { useRef } from "react";
import "./styles.css";

export default function App() {
const outerRef = useRef();
return (
<div className="App">
<div
onBlur={(e) => {
// 失焦后获取焦点的那个元素
const nextFocusDom = e.relatedTarget;
// 判断其是否在父元素内,如果在父元素内就不错处理
if (nextFocusDom && outerRef.current.contains(nextFocusDom)) {
console.log(" [内部导致] 父元素失焦");
} else {
console.log(" [需要处理] [外部导致] 父元素失焦");
}
}}
className="outer"
ref={outerRef}
tabIndex="0"
>
<input className="inner" />
<select className="inner" />
</div>
<input />
</div>
);
}
baxtergu
2021-01-14 11:48:47 +08:00
MDN 上关于 relatedTarget 的解释: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget

不同事件的 relatedTarget 不太一样
sillydaddy
2021-01-14 12:38:06 +08:00
@hjylxmhzq
@baxtergu
谢谢。感觉 relatedTarget 这个更好用。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/744655

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX