Jimmy
Jimmy

Reputation: 3880

Trigger an event only if it is outside of a dom element that has a specific class

I have an event listener that listens to clicks. If these clicks are outside of a given dom element, a certain function is called (in this case, handleClick).

Right now, the function handleClick is only called if the event target is not the one that has the this.setWrapper ref.

I am wondering how I can prevent handleClick from being called if I also happen to click on .boxTwo.

Note, I want to do this not by adding a ref to the div of .boxTwo, but by querying to DOM to identify any clicks that happen inside of the div that has the class name 'boxTwo'. Thanks!

class App extends React.Component {
  constructor() {
    super();
    this.state = {onClick: false};
    this.setWrapperRef = this.setWrapperRef.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

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

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

  setWrapperRef(node) {
    this.wrapperRef = node;
  }

  handleClick(event) {
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      this.setState({onClick: !this.state.onClick});
    }
  }

  render() {
    return (
      <section>
        <div className="boxOne" ref={this.setWrapperRef} />
        <div className="boxTwo" />
        <div>{this.state.onClick ? "On" : "Off"}</div>
      </section>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));
.boxOne {
  height: 100px;
  width: 100px;
  background: pink;
}

.boxTwo {
  margin: 30px;
  height: 50px;
  width: 70px;
  border: solid black 2px;
  background: orange;
}
<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>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="app"></div>

Upvotes: 0

Views: 62

Answers (2)

aquiseb
aquiseb

Reputation: 1009

  • You just need to check if event.target.className does not equal boxTwo, like this snippet below.
  • You may use the same "technique" to prevent your function from being called if the target has a boxOne class.
  • If you want to trigger in all cases where there is no className at all, you may use just this in your conditional statement: !event.target.className

A little extra, I would have done it this way for the sake of readability

handleClick(event) {

    // The ones that don't trigger
    const disabledTriggers = ['boxOne', 'boxTwo'];

    // Ok if no class contained in our event classList are disabled
    const ok = !disabledTriggers.some(c => event.target.classList && event.target.classList.contains(c))
    if(ok) {
      this.setState({onClick: !this.state.onClick});
    }
  }

And here's your snippet with the solution and the least modifications.

class App extends React.Component {
  constructor() {
    super();
    this.state = {onClick: false};
    this.setWrapperRef = this.setWrapperRef.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

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

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

  setWrapperRef(node) {
    this.wrapperRef = node;
  }

  handleClick(event) {
    
    if (event.target.className !== 'boxTwo' && this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      this.setState({onClick: !this.state.onClick});
    }
  }

  render() {
    return (
      <section>
        <div className="boxOne" ref={this.setWrapperRef} />
        <div className="boxTwo" />
        <div>{this.state.onClick ? "On" : "Off"}</div>
      </section>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));
.boxOne {
  height: 100px;
  width: 100px;
  background: pink;
}

.boxTwo {
  margin: 30px;
  height: 50px;
  width: 70px;
  border: solid black 2px;
  background: orange;
}
<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>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="app"></div>

Upvotes: 1

PassionInfinite
PassionInfinite

Reputation: 638

Let me explain what I have done.

  1. Added one state to maintain the classes for the elements on which we don't want to have onclick.
  2. Added one more container class to the boxOne class to check that this holds true even if we have more than one class.
  3. I have check that event.target.classList(which returns the list of the class current target have) and check that anyone from the classList exists to the state wrapperRefs.
  4. If it does not exists then it is click outside the component. I hope this helps you.

    If you have any doubt comment below. I would like to help you as much as I can.

class App extends React.Component {
  constructor() {
    super();
    this.state = {onClick: false, wrapperRefs: ['boxOne', 'boxTwo']};
    this.handleClick = this.handleClick.bind(this);
  }

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

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

  handleClick(event) {
    const classes = event.target.classList;
    const exists = this.state.wrapperRefs.some(ref => classes.contains(ref));
    if(!exists) {
      this.setState({onClick: !this.state.onClick});
    }
  }

  render() {
    return (
      <section>
        <div className="boxOne container" />
        <div className="boxTwo" />
        <div>{this.state.onClick ? "On" : "Off"}</div>
      </section>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));
.boxOne {
  height: 100px;
  width: 100px;
  background: pink;
}

.boxTwo {
  margin: 30px;
  height: 50px;
  width: 70px;
  border: solid black 2px;
  background: orange;
}
<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>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="app"></div>

Upvotes: 0

Related Questions