Sonya
Sonya

Reputation: 103

React: Close a modal clicking outside

I have a modal reaction component with a button that opens the modal. The modal also has a close button. Here is the code and what the modal looks like:

class Modal extends React.Component {
    static defaultProps = {
        title: "Modal Title",
        float: false,
        color: "pink",
        children: <div>Add children here!</div>
    }

    constructor(props) {
        super(props)
        this.state = {
            show: false
        }
    }

    openModal() {
        this.setState(
            { show: true }
        )
    }

    closeModal() {
        this.setState(
            { show: false }
        );
    }

    render() {
        return (
            <div>
                <button className={"modal-button" + " " + this.props.color} onClick={() => this.openModal()}>{this.props.title}</button>
                {this.state.show && <div className={"modal fade-in" + " " + (this.props.float ? "modal-float" : null)}>
                    <div className="modal-head">
                        <span>{this.props.title}</span>
                        <button id='button' onClick={() => this.closeModal()}>x</button>
                    </div>
                    <div className="modal-body">
                        {this.props.children}
                    </div>
                </div>}
            </div>
        )
    }
}

It has a button that opens the modal. And the modal has a close button too.

I would like close the modal clicking outside. In another question I saw a code like this

handleClick = event => {
    event.preventDefault();

    this.setState({ showModal: true }, () => {
      document.addEventListener("click", this.closeMenu);
    });
  };

closeMenu = () => {
    this.setState({ menuOpen: false }, () => {
      document.removeEventListener('click', this.closeMenu);
    });
  }

But it also close the modal when I click inside the modal.

Upvotes: 4

Views: 6046

Answers (1)

terraforme
terraforme

Reputation: 458

You need to add a condition that doesn't close the modal when the modal is open and a click originates inside the modal. To detect where a click is happening, you will probably use refs.

https://reactjs.org/docs/refs-and-the-dom.html

For ex, I added this condition in the closeMenu function:

this.modalRef = React.createRef();

handleClick = event => {
    event.preventDefault();

    this.setState({ showModal: true }, () => {
      document.addEventListener("click", this.closeMenu);
    });
  };

closeMenu = () => {
  if(this.modalRef.current && this.modalRef.current.contains(event.target)) {
    return; 
}
    this.setState({ menuOpen: false }, () => {
      document.removeEventListener('click', this.closeMenu);
    });
  }


render() {
    return (
      <div ref={this.modalRef}>
       // modal component 
      </div>
    );
  }
}

This detects 1. if the modal is open (this.modalRef.current) and 2. if the click originates inside the modal (this.modalRef.current.contains(event.target)), in which case it returns and does not close the modal.

This explains the approach, although it uses hooks not class components, the concept is the same: https://javascript.plainenglish.io/detect-a-click-outside-of-a-react-component-with-a-reusable-hook-and-useref-a0c282171c3f

Upvotes: 2

Related Questions