Celdric Kang
Celdric Kang

Reputation: 399

Programmatically cause onBlur to trigger in react

I use onBlur to close a dropdown, but I also want to handle a click handler of an li which is render within, setState won't work here, the behavior is broken when user try to open the dropdown again, try it here:

http://jsfiddle.net/ur1rbcrz

My code:

toggleDropdown = () => {
    this.setState({
        openDropdown: !this.state.openDropdown
    })
  }

  render() {
    return (
      <div>
        <div tabIndex="0" onFocus={this.toggleDropdown} onBlur={this.toggleDropdown}>
          MyList
        <ul className={this.state.openDropdown ? 'show' : 'hide'}>
          <li>abc</li>
          <li>123</li>
          <li onClick={()=> this.setState({openDropdown:false})}>xyz</li> {/* not working */}
        </ul>
      </div>
      </div>
    );
  }

Upvotes: 4

Views: 26178

Answers (3)

Marlon da Veiga
Marlon da Veiga

Reputation: 618

OnBlur is a React Synthetic event and can be used in two ways:

  1. To trigger something:

const {useState} = React;

const Example = ({title}) => {
  const [field, setField] = useState("");

  return (
    <div>
      <p>{title}</p>
      <p>Uppercase on blur</p>
      <input type="text" 
      value={field} 
      onChange={e=>setField(e.target.value)}
      //LOOK HERE !
      onBlur={e=>setField(e.target.value.toUpperCase())}
      />
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example title="OnBlur triggering:" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  1. Be triggered by something

const {
  useState,
} = React;

const Example = ({
  title
}) => {
  const [field, setField] = useState("");

  return ( <
    div >
    <
    p > {
      title
    } < /p> <
    p > Remove focus by pressing enter < /p> <
    input type = "text"
    value = {
      field
    }
    onChange = {
      e => setField(e.target.value)
    }
    //LOOK HERE !
    onBlur = {
      e => setField(e.target.value.toUpperCase())
    }
    onKeyPress = {
      e => (e.key === 'Enter' ? setField(e.target.value.toLowerCase()) || e.target.blur() : null)
    }
    /> < /
    div >
  );
};

// Render it
ReactDOM.render( <
  Example title = "OnBlur triggered:" / > ,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

So to programmatically cause onBlur to trigger in react is necessary add an event to watch your change.

More info: React SyntheticEvents

Upvotes: 0

Elias Ghali
Elias Ghali

Reputation: 863

i added onClick event to your div and it worked, your code becomes:

  render() {
return (
  <div>
    <div tabIndex="0" onClick={() => this.setState({openDropdown: !this.state.openDropdown})} onFocus={this.toggleDropdown} onBlur={this.toggleDropdown}>
      MyList
    <ul className={this.state.openDropdown ? 'show' : 'hide'}>
      <li>abc</li>
      <li>123</li>
      <li onClick={()=> this.setState({openDropdown:false})}>xyz</li> {/* not working */}
    </ul>
  </div>
  </div>
);

}

Upvotes: 0

loelsonk
loelsonk

Reputation: 3596

Your code is not working because, even though you click li, a div container with onBlur event still is focused.

We add to your list container ref, after that we can call .blur(). We use it in your onClick li event handler.

this.dropDownList.blur()

See working example jsfiddle.

Or run this snippet:

class Hello extends React.Component {
  constructor() {
    super()
    this.state = {
      isDropdownVisible: false
    }
    
    this.toggleDropdown = this.toggleDropdown.bind(this);
  }
  
  toggleDropdown() {
  	this.setState({
    	isDropdownVisible: !this.state.isDropdownVisible
    })
  }
  
  render() {
    return (
      <div>
        <div 
        tabIndex="0" 
        ref={c => this.dropDownList = c}
        onFocus={this.toggleDropdown} 
        onBlur={this.toggleDropdown}>
          MyList
        <ul
        className={this.state.isDropdownVisible ? 'show' : 'hide'}>
          <li>abc</li>
          <li>123</li>
          <li onClick={() => this.dropDownList.blur()}>xyz</li> {/* not working */}
        </ul>
      </div>
      </div>
    );
  }
}

ReactDOM.render(
  <Hello initialName="World"/>,
  document.getElementById('container')
);
.hide {
  display: none
}

.show {
  display: block !important;
}

div:focus {
  border: 1px solid #000;
}

div:focus {
		outline: none;
	}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
  <!-- This element's contents will be replaced with your component. -->
</div>

Upvotes: 6

Related Questions