V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sillydaddy
V2EX  ›  React

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

  •  
  •   sillydaddy · 2021-01-13 18:14:11 +08:00 · 3069 次点击
    这是一个创建于 1392 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近在用 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 使用的是冒泡模型,后来的标准里面就有了这两种模型可选”,——感觉不太相信,仅仅是为了兼容? 有没有比较好的这方面相关的资料呢? )
    7 条回复    2021-01-14 12:38:06 +08:00
    xiaoming1992
        1
    xiaoming1992  
       2021-01-13 19:27:51 +08:00
    preventDefault ?
    xiaoming1992
        2
    xiaoming1992  
       2021-01-13 19:31:14 +08:00
    不过在`react 17`中改用了浏览器的`focusin`, `focusout`
    hjylxmhzq
        4
    hjylxmhzq  
       2021-01-13 22:41:55 +08:00
    这种我一般用两个方法,一个是你说的全局监听 click,另一个是在 onBlur 中给一个 setTimeout,然后在同一个节点的 onFocus 中取消掉计时器
    baxtergu
        5
    baxtergu  
       2021-01-14 11:47:10 +08:00   ❤️ 1
    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
        6
    baxtergu  
       2021-01-14 11:48:47 +08:00
    MDN 上关于 relatedTarget 的解释: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget

    不同事件的 relatedTarget 不太一样
    sillydaddy
        7
    sillydaddy  
    OP
       2021-01-14 12:38:06 +08:00
    @hjylxmhzq
    @baxtergu
    谢谢。感觉 relatedTarget 这个更好用。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1065 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 49ms · UTC 19:56 · PVG 03:56 · LAX 11:56 · JFK 14:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.