Reputation: 2417
I am trying to create a customizable generic scalable modal using reactjs. I know there is tons of library but I am just developing for understanding the react ecosystem. However, in my modal, when i clicked on inside the modal content, the modal gets closed. The modal should close only when clicking on the close button and outside the modal area.
Here is what I have done. The demo is there too
https://codepen.io/anon/pen/mzMdoq
Code
class Portal extends React.Component {
render() {
return ReactDOM.createPortal(
<div
style={{
position: "absolute",
top: "0",
bottom: "0",
left: "0",
right: "0",
display: "grid",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0,0,0,0.3)"
}}
onClick={this.props.onClose}
>
<div
style={{
padding: 20,
background: "#fff",
borderRadius: "2px",
display: "inline-block",
minHeight: "300px",
margin: "1rem",
position: "relative",
minWidth: "300px",
boxShadow: "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)",
justifySelf: "center"
}}
>
{this.props.children}
<hr />
<button onClick={this.props.onClose}>Close</button>
</div>
</div>,
document.body
);
}
}
class Modal extends React.Component {
modalRef = React.createRef();
componentDidMount() {
document.addEventListener("mousedown", this.handleClick, false);
}
componentWillUnmount() {
document.removeEventListener("mousedown", this.handleClick, false);
}
handleClick = e => {
const node = this.modalRef.current;
console.log("props", node);
if (
this.modalRef &&
this.modalRef.current &&
this.modalRef.current.contains(e.target)
) {
return;
} else {
this.props.onClose();
}
};
render() {
return (
<React.Fragment>
<Portal {...this.props} ref={this.modalRef} />
</React.Fragment>
);
}
}
class ModalExample extends React.Component {
state = { showModal: false };
showModal = () => this.setState({ showModal: true });
handleClose = () => this.setState({ showModal: false });
render() {
const { showModal } = this.state;
return (
<React.Fragment>
<button onClick={this.showModal}>Modal</button>
{showModal ? (
<Modal showModal={showModal} onClose={this.handleClose}>
This is the modal content!
</Modal>
) : null}
</React.Fragment>
);
}
}
function App() {
return (
<div className="App">
<h1>Hello</h1>
<ModalExample />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Upvotes: 1
Views: 2922
Reputation: 4068
Your overlay div wraps the modal content div, so clicking on content is also considered clicking on the overlay.
One way to solve this is to not nest modal content inside overlay, but move it outside:
return ReactDOM.createPortal(
<div style={{
position: "absolute",
top: "0",
bottom: "0",
left: "0",
right: "0",
display: "grid",
justifyContent: "center",
alignItems: "center"
}}>
<div
style={{
position: "absolute",
top: "0",
bottom: "0",
left: "0",
right: "0",
display: "grid",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0,0,0,0.3)"
}} onClick={this.props.onClose}
/>
<div
style={{
padding: 20,
background: "#fff",
borderRadius: "2px",
display: "inline-block",
minHeight: "300px",
margin: "1rem",
position: "relative",
minWidth: "300px",
boxShadow: "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)",
justifySelf: "center"
}}
>
{this.props.children}
<hr />
<button onClick={this.props.onClose}>Close</button>
</div>
</div>,
document.body
);
Upvotes: 0
Reputation: 1027
Hmm this is not exactly an answer but still if it helps...
I usually generate modals this way
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_modal
Here as you can see the modal already exists in the DOM and is hidden or displayed as required using js. It's a bit different to yours as the div is pre-existing.
Upvotes: 0
Reputation: 1385
Add e.stopPropagation() to onClick of your modal's body. Something like this
<div
style={{
padding: 20,
background: "#fff",
borderRadius: "2px",
display: "inline-block",
minHeight: "300px",
margin: "1rem",
position: "relative",
minWidth: "300px",
boxShadow: "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)",
justifySelf: "center"
}}
onClick={e => e.stopPropagation()}
>
{this.props.children}
<hr />
<button onClick={this.props.onClose}>Close</button>
</div>
Upvotes: 3
Reputation: 4394
You should remove onClick={this.props.onClose}
inside ReactDOM.createPortal
and it will work fine
Upvotes: 0