Last active
January 1, 2018 13:14
-
-
Save sogko/be2fcd4403c02875f3b2 to your computer and use it in GitHub Desktop.
A pure ES6-style composable React component that handles clicks outside of a HTML node / React component. (No mixins)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
A pure ES6-style composable React component that handles clicks outside of a HTML node. | |
This is for those who prefers composibility over mixins. | |
Simply drop-in the event listener component into your React component. | |
Adapted from: https://github.com/Pomax/react-onclickoutside | |
*/ | |
var React = require('react'); | |
var IGNORE_CLASS = 'ignore-react-onclickoutside'; | |
class ClickOutsideListener extends React.Component { | |
constructor() { | |
super(); | |
} | |
_generateOutsideClickHandler(localNode, eventHandler) { | |
return function(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); | |
}; | |
} | |
componentWillUnmount() { | |
this.disableOnClickOutside(); | |
this.__outsideClickHandler = false; | |
} | |
/** | |
* Make this call in parent's componentDidMount to register parent node's handler | |
* to handle clicks outside of parent node | |
* Example: | |
* ... | |
* componentDidMount() { | |
* this.refs.myClickOutsideListener.registerOnClickOutside(React.findDOMNode(this)) | |
* } | |
* render() { | |
* return ( | |
* <div> | |
* <ClickOutsideListener ref="myClickOutsideListener" onClickOutside={...}/> | |
* ... | |
* </div> | |
* } | |
*/ | |
registerOnClickOutside(localNode) { | |
this.__outsideClickHandler = this._generateOutsideClickHandler(localNode, this.props.onClickOutside); | |
this.enableOnClickOutside(); | |
} | |
/** | |
* Can be called to explicitly enable event listening | |
* for clicks and touches outside of this element. | |
*/ | |
enableOnClickOutside() { | |
var fn = this.__outsideClickHandler; | |
document.addEventListener('mousedown', fn); | |
document.addEventListener('touchstart', fn); | |
} | |
/** | |
* Can be called to explicitly disable event listening | |
* for clicks and touches outside of this element. | |
*/ | |
disableOnClickOutside() { | |
var fn = this.__outsideClickHandler; | |
document.removeEventListener('mousedown', fn); | |
document.removeEventListener('touchstart', fn); | |
} | |
render() { | |
return <div style={{display: 'none'}}>{this.props.children}</div>; | |
} | |
} | |
ClickOutsideListener.propTypes = { | |
children: React.PropTypes.node, | |
onClickOutside: React.PropTypes.func.isRequired | |
}; | |
ClickOutsideListener.defaultProps = { | |
}; | |
module.exports = ClickOutsideListener; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require('react'); | |
var ClickOutsideListener = require('./ClickOutsideListener.react'); | |
class Dropdown extends React.Component { | |
constructor() { | |
super(); | |
this.handleClickOutside = this.handleClickOutside.bind(this); | |
} | |
componentDidMount() { | |
// register onClickOutside listener | |
var thisNode = React.findDOMNode(this); | |
this.refs.clickOutsideListener.registerOnClickOutside(thisNode); | |
} | |
handleClickOutside(ev) { | |
console.log('Clicked outside'); | |
} | |
render() { | |
return ( | |
<div> | |
<ClickOutsideListener ref="clickOutsideListener" onClickOutside={this.handleClickOutside}/> | |
<button>This is a button</button> | |
</div> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment