How to implement onBlur/onFocus for a div with nested input fields?

11,848

Solution 1

Just adding on to this with what I think is the best solution these days.

This ignores blur events by using the Node.contains method to check whether the element is a descendant of that which is already focused.

handleBlur({ currentTarget, relatedTarget }) {
   if (currentTarget.contains(relatedTarget)) return;

   /* otherwise normal actions to perform on blur */

   console.log('blur');
}
handleFocus(e) {
   console.log('focus');
}

Solution 2

You may want to ignore extra blur events.

handleBlur(e) {
   if (e.target.tagName == "INPUT") {
      return;
   }
   console.log('blur');
}
handleFocus(e) {
   console.log('focus');
}

Solution 3

How about splitting the inputs into a single, independent component?

app.js

class Thing extends React.Component {
  handleBlur(val, event) {
    console.log(val, event);
  }
  handleFocus(val, event) {
    console.log(val, event);
  }

  data = ['Hello, ', 'Thing'];

  render() {
    return (
      <div tabIndex="1">
        {this.data.map((v, i) => <Input value={v} key={i} onFocus={this.handleFocus} onBlur={this.handleBlur} />)}
      </div>
    );
  }
}

Input.js

import React from 'react';

export class Input extends React.PureComponent {

  handleF = () => {
    this.props.onFocus(this.props.value, 'focus');
  }

  handleB = () => {
    this.props.onBlur(this.props.value, 'blur');
  }

  render() {
    return <input type="text" onFocus={this.handleF} onBlur={this.handleB} />;
  }
}

export default Input;

https://codesandbox.io/s/J6o5Ey9Jg

Solution 4

This is from OP comments above, he found the following solution which worked for him (and now me) and posted it in a comment. I am reposting it here for anyone else who might not think to dig through comments.

Basically, I just check on every blur event if e.relativeTarget has e.currentTarget anywhere in the parentElement chain.

https://codesandbox.io/s/vo2opP0Nn?file=/index.js

Share:
11,848
dmigo
Author by

dmigo

Strange days have tracked us down.

Updated on June 04, 2022

Comments

  • dmigo
    dmigo about 2 years

    There is a <div> and a couple of nested <input>s. onBlur fires every time user clicks on one of the <input>s.
    This is a bit frustrating that onBlur happens when I hit something inside the div. After an hour of searching I still wasn't able to find any good solution.
    This sample of code shows what I'm talking about:

    class Thing extends React.Component {
      handleBlur(e) {
        console.log('blur');
      }
      handleFocus(e) {
        console.log('focus');
      }
      render() {
        return (
          <div onFocus={this.handleFocus} onBlur={this.handleBlur} tabIndex="1">
            <div>
              <input type="text" value="Hello," />
            </div>
            <div>
              <input type="text" value="Thing" />
            </div>
          </div>
        );
      }
    }
    

    You may play around with the code over here.
    However my ultimate goal is to make this thing working properly.