Reputation: 399
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:
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
Reputation: 618
OnBlur is a React Synthetic event and can be used in two ways:
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>
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
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
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