I have a Toggler button
which opens a drop-down menu. To add a functionality where if the user clicks anywhere on the screen other than that menu, the drop-down menu should get closed. So, for this, I need to create click handler and keep the record of the node from which the click has happened. If the click has been from outside the drop-down node, the menu should get closed. This is the code:
state = {
menuTogglerOpen: false
}
componentWillMount() {
document.addEventListener("mousedown", this.handleClick, false)
}
componentWillUnmount() {
document.removeEventListener("mousedown", this.handleClick, false)
}
handleClick = event => {
if (this.togglerNode.contains(event.target)) {
return
}
this.closeMenuTogglerFromOutside()
}
closeMenuTogglerFromOutside = () => {
this.setState({
menuTogglerOpen: false
})
}
handleMenuToggler = () => {
this.setState({
menuTogglerOpen: !this.state.menuTogglerOpen
})
}
JSX
<Toggler ref={currentNode=> (this.togglerNode = currentNode)}>
<button onClick={this.handleMenuToggler}>
<svg
...button SVG...
</svg>
</button>
{this.state.menuTogglerOpen && (
<TogglerDropDown>
<div className="_dropdown_caret" />
<div className="_dropdown">
<ul>
<li>puspender</li>
<li>settings</li>
<li>help</li>
<li>logout</li>
</ul>
</div>
</TogglerDropDown>
)}
</Toggler>
</HeaderGroup>
</SiteHeaderRight>
On this approach, every click(anywhere on screen) calls the handleClick method. Could it impact the application performance? If yes, what is the better way to handle such event?
Update
As suggested by Alexei, this is the solution I implemented:
handleClick = event => {
if (this.togglerNode.contains(event.target)) {
return
}
this.closeMenuToggler()
}
openMenuToggler = () => {
document.addEventListener("mousedown", this.handleClick, false)
this.setState({
menuTogglerOpen: true
})
}
closeMenuToggler = () => {
document.removeEventListener("mousedown", this.handleClick, false)
this.setState({
menuTogglerOpen: false
})
}
handleMenuToggler = () => {
if (this.state.menuTogglerOpen) {
this.closeMenuToggler()
} else {
this.openMenuToggler()
}
}
Comment:
You could attach the event listener when the menu opens, and remove it when the menu closes. Traditionally that's how most modals are implemented. Only when the modal is open do you want to listen for the click-to-close event.
This may or may not apply to your code base but you may want to also consider removing the event listener on componentWillUnmount
that way you avoid any potential memory leaks. This covers the case where the component unmounts without calling closeMenuToggler
in which case you will have left the stale event listener running.
componentWillUnMount() {
if (this.state.menuTogglerOpen) {
document.removeEventListener("mousedown", this.handleClick, false)
}
}
handleClick = event => {
if (this.togglerNode.contains(event.target)) {
return
}
this.closeMenuToggler()
}
openMenuToggler = () => {
document.addEventListener("mousedown", this.handleClick, false)
this.setState({
menuTogglerOpen: true
})
}
closeMenuToggler = () => {
document.removeEventListener("mousedown", this.handleClick, false)
this.setState({
menuTogglerOpen: false
})
}
handleMenuToggler = () => {
if (this.state.menuTogglerOpen) {
this.closeMenuToggler()
} else {
this.openMenuToggler()
}
}