Skip to content

react onclickoutside

Created: 2017-08-15 10:52:58 -0700 Modified: 2017-09-13 11:39:22 -0700

Frequently I will want a pop-up or something to close when you click outside of it. This library can be used for that.

This lets you click outside of two open popups but only close one of them. This is achieved using react-onclickoutside’s disableOnClickOutside property.

HiDeoo just added a boolean for whether to ignore the onClickOutside function altogether: https://codesandbox.io/s/k961x4z0qr

Having a button open/close a pop-up correctly

Section titled Having a button open/close a pop-up correctly

I had a situation like this in Bot Land:

Clicking “Primary color” or “Secondary color” was supposed to pop up the color-picker (as shown above). Clicking outside of the color picker was supposed to close it. That’s very straightforward to set up with react-onclickoutside. The problem I ran into was when the color picker is open, clicking the button again should close it. However, if you try this via the naïve approach, react-onclickoutside will get hit first (which will close the pop-up), then another event will trigger on the button, so the net result is that the pop-up closes and reopens.

To fix this, react-onclickoutside provides a prop called “outsideClickIgnoreClass” (which defaults to “ignore-react-onclickoutside”). Any element with that CSS class will be ignored when it comes to outside clicks, so if you added “ignore-react-onclickoutside” to the button that opens the color pop-up, then it would be fixed.

However, there’s one more caveat. In the case above, there are two buttons that can spawn a color picker, so if you just added “ignore-react-onclickoutside” to both, then clicking “Secondary color” above would open its pop-up without closing the existing pop-up. To get around this, I specified the prop to be a unique class name by using Lodash’s _.uniqueId function as shown (the example is a relatively complete example of all different aspects of usage, but it also shows uniqueId being called):

import { SwatchesPicker } from 'react-color';
const _ = require('lodash');
const onClickOutside = require('react-onclickoutside');
class ColorPickerPopup extends Component {
/**
* This gets called by react-onclickoutside.
*/
handleClickOutside(evt) {
this.props.onClose();
}
/**
* We don't want clicks in the background of SwatchesPicker to bubble up to
* togglePopupVisibility in the parent DOM elements.
*/
catchClicks(event) {
event.stopPropagation();
}
render() {
return (
<div onClick={this.catchClicks.bind(this)}>
<SwatchesPicker onChange={this.props.onChangeColor}/>
</div>
);
}
}
const WrappedColorPickerPopup = onClickOutside(ColorPickerPopup);
class ColorPicker extends Component {
constructor(props) {
super(props);
this.state = {
showPopup: false
};
this.ignoreOnClickOutsideClass = _.uniqueId('onclickoutside');
}
hidePopup() {
this.setState({
showPopup: false
});
}
togglePopupVisibility() {
this.setState((prevState) => ({
showPopup: !prevState.showPopup
}));
}
render() {
return (
<div
className={this.ignoreOnClickOutsideClass}
onClick={this.togglePopupVisibility.bind(this)}
>
{/*some DOM elements removed (e.g. buttons/flex/whatever) to show important parts only*/}
{this.state.showPopup &&
<WrappedColorPickerPopup
outsideClickIgnoreClass={this.ignoreOnClickOutsideClass}
onClose={this.hidePopup.bind(this)}
/>
}
</div>
);
}
}