Skip to content

Instantly share code, notes, and snippets.

@schovi
Last active September 8, 2016 10:25
Show Gist options
  • Save schovi/e3780466f550877c9c16 to your computer and use it in GitHub Desktop.
Save schovi/e3780466f550877c9c16 to your computer and use it in GitHub Desktop.
import React from 'react';
const IGNORE_CLASS = 'ignore-react-onclickoutside';
export default function clickOutside(BaseComponent) {
return class ClickOutside extends React.Component {
static displayName = `${BaseComponent.name}ClickOutside`;
constructor(props) {
super(props)
}
componentDidMount() {
const component = this.refs.component
const componentHandler = component.onClickOutside
if(!componentHandler)
throw new Error("Component lacks a onClickOutside(event) function for processing outside click events.");
const makeHandler = (localNode, eventHandler) => {
return (evt) => {
var source = evt.target;
var found = false;
// If source=local then this event came from "somewhere"
// inside and should be ignored. We could handle this with
// a layered approach, too, but that requires going back to
// thinking in terms of Dom node nesting, running counter
// to React's "you shouldn't care about the DOM" philosophy.
while(source.parentNode) {
found = (source === localNode || source.classList.contains(IGNORE_CLASS));
if(found) return;
source = source.parentNode;
}
eventHandler(evt);
}
}
this.__outsideClickHandler = makeHandler(React.findDOMNode(this), componentHandler.bind(component));
// If there is a truthy disableOnClickOutside property for this
// component, don't immediately start listening for outside events.
if (!this.props.disableOnClickOutside) {
this.enableOnClickOutside();
}
}
componentWillUnmount() {
this.disableOnClickOutside();
this.__outsideClickHandler = undefined;
this.__callback = undefined
}
/**
* Can be called to explicitly enable event listening
* for clicks and touches outside of this element.
*/
enableOnClickOutside() {
const fn = this.__outsideClickHandler;
document.addEventListener("mousedown", fn);
document.addEventListener("touchstart", fn);
}
handleClickOutside(ev) {
handler && handler(ev)
}
/**
* Can be called to explicitly disable event listening
* for clicks and touches outside of this element.
*/
disableOnClickOutside() {
const fn = this.__outsideClickHandler;
document.removeEventListener("mousedown", fn);
document.removeEventListener("touchstart", fn);
}
render() {
return <BaseComponent {...this.props} ref="component"/>;
}
};
}
import React from 'react';
import clickOutside from './click_outside_decorator'
@clickOutside
export default class MyComponent extends React.Component {
onClickOutside() {
console.log("Click outside")
}
onClickInside() {
console.log("Click inside")
}
render() {
return (
<div onClick={this.onClickInside}>
...
</div>
);
}
}
@gustavoguichard
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment