18

I have a component called Dialog, in which I attach an event listener on mouse clicks on the window object.

componentDidMount() {
    document.addEventListener('click', this.handleClick);
}

componentWillUnmount() {
    document.removeEventListener('click', this.handleClick);
}

How can I detect (in the handleClick function) whether a click has been fired inside the component or outside? Note that this dialog contains different elements and child components.

Matthew
  • 10,988
  • 11
  • 54
  • 69
  • I found this question about detecting clicks outside a component. http://stackoverflow.com/questions/32553158/detect-click-outside-react-component I guess you can workaround and detect clicks inside a component. – Hedegare Nov 09 '16 at 13:21
  • @Jackowski thanks I'll try that. The accepted answer (author itself) states that he had a problem attaching the click callback to the document body : "Because the rerender from React happens before the document body handler is called, the element was not detected as "inside" the tree." Any idea why? – Matthew Nov 09 '16 at 13:28

1 Answers1

27

parent.contains(child) is your friend. This solution using refs might not be perfect, but simply using this does not work as it's not a proper DOM node. I'm using React 15 here, so keep in mind that in earlier versions you'd have to use .getDOMNode() on the parent.

class Dialog extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }
  componentDidMount() {
    document.addEventListener('click', this.handleClick);
  }
  componentWillUnmount() {
    document.removeEventListener('click', this.handleClick);
  }
  handleClick(e) {
    if (this.node.contains(e.target)) {
      console.log('You clicked INSIDE the component.')
    } else {
      console.log('You clicked OUTSIDE the component.')
    }
  }
  render() {
    return(
      <span ref={node => this.node = node}>
        Level 0<br/>
        <span>
          Level 1.<br/>
          <span>Level 2.</span>
        </span>
      </span>
    );
  }
}

ReactDOM.render(<Dialog/>, document.getElementById('View'));
<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="View"></div>
Fabian Schultz
  • 18,138
  • 5
  • 49
  • 56
  • Thanks! Maybe you guessed that I try to make the dialog a modal dialog. However by implementing the code I realized I completely screwed up: if I click on an element outside the dialog, then firstly the event handler on that element will be executed, causing to re-render and i'll lose the dialog... What I was hoping was that the "handleClick" callback would have priority above all, so that I can stop the event to be further processed if clicked outside the dialog... I think my idea won't work actually... Any advice? – Matthew Nov 09 '16 at 17:11
  • 1
    Maybe `e.preventDefault()` helps, but I think in this case you need a different approach. I'm sure there are many examples online when you search for "react modals". – Fabian Schultz Nov 09 '16 at 20:24