简体   繁体   中英

How do I retain focus when the user clicks a menu?

I have the following requirements:

  • Display an input field.
  • Display a menu.
  • When the input field has focus, show the menu.
  • If the input field looses focus, hide the menu.
  • If the menu is clicked, retain the menu even though the input field has lost focus.

To reproduce my issue: - Place cursor in the input field - Click the menu

Expected: - The menu is still visible

Actual: - The menu disappears

This should be straightforward in React, but I haven't been able to solve this in the most obvious way and would like to know what it is I'm overlooking.

class App extends React.Component {
  constructor() {
    super();
    this.state = {};
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
  }

  handleFocus() {
    this.setState({ hasFocus: true });
  }

  handleBlur() {
    this.setState({ hasFocus: false});
  }

  handleMenuClick() {
    this.setState({ hasFocus: true });
  }

  render() {
    const { hasFocus } = this.state;

    const menuInstance = (
      <div
        onClick={this.handleMenuClick}
        tabIndex="0"
      >
        Some menu
      </div>
    );

    return (
      <div>
        <h1>Focus issue</h1>
        <p>How do I retain focus if the user clicks on the menu item?</p>
        <input
          onBlur={this.handleBlur}
          onFocus={this.handleFocus}
          defaultValue="some value"
        />
        { hasFocus && menuInstance }
      </div>
    );
  }
}

Live demo here: http://jsbin.com/zixuhoj/6/edit?js,console,output

You're listening for blur and click events. Blur events are fired before click events so your handleMenuClick will never be called.

Instead you can listen to onMouseDown and set a flag for menuClicked or something like that and look at that flag in the blur event.

Demo can be found here: http://jsbin.com/buzisepafu/1/edit?js,console,output

class App extends React.Component {
  constructor() {
    super();
    this.state = {};
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.handleMenuClick = this.handleMenuClick.bind(this);
  }

  handleFocus() {
    this.setState({ hasFocus: true });
  }

  handleBlur() {
    if (!this.state.menuClicked) {
      this.setState({ hasFocus: false});
    }
  }

  handleMenuClick (event) {
    this.setState({ menuClicked: true });
  }

  render() {
    const { hasFocus } = this.state;

    const menuInstance = (
      <div
        className="menu"
        onMouseDown={this.handleMenuClick}
        tabIndex="0"
      >
        Some menu
      </div>
    );

    return (
      <div>
        <h1>Focus issue</h1>
        <p>How do I retain focus if the user clicks on the menu item?</p>
        <input
          onBlur={this.handleBlur}
          onFocus={this.handleFocus}
          defaultValue="some value"
        />
        { hasFocus && menuInstance }
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));

I found a different approach that works in my environment where the users only have Chrome where I put the onBlur handler on the outer div and check if the relatedElement on the blur event is inside the outer div or not.

Feel free to ask if you'd like me to elaborate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM