[英]React close modal on click outside
我在没有任何库的情况下使用 React 创建了一个基本模态,它运行良好,现在当我在模态外部单击时,我想关闭模态。
这是CodeSandbox实时预览
我的索引.js:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
showModal: false
};
}
handleClick = () => {
this.setState(prevState => ({
showModal: !prevState.showModal
}));
};
render() {
return (
<>
<button onClick={this.handleClick}>Open Modal</button>
{this.state.showModal && (
<div className="modal">
I'm a modal!
<button onClick={() => this.handleClick()}>close modal</button>
</div>
)}
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
使用ref
,这会有点棘手
看这个CodeSandBox
您可以通过为与模态主体相邻的模态背景创建一个 div 来实现。 使用 position 绝对值和 100% 高度和宽度值使其覆盖整个屏幕。
这样,模态体就坐在背景上。 如果您单击模态主体,则不会发生任何事情,因为背景没有收到单击事件。 但是如果你点击背景,你可以处理点击事件并关闭模态框。
关键是模态背景不会包裹模态体,而是位于它旁边。 如果它包裹身体,那么任何点击背景或身体都会关闭模式。
const {useState} = React; const Modal = () => { const [showModal,setShowModal] = useState(false) return ( <React.Fragment> <button onClick={ () => setShowModal(true) }>Open Modal</button> { showModal && ( <React.Fragment> <div className='modal-backdrop' onClick={() => setShowModal(false)}></div> <div className="modal"> <div>I'm a modal.</div> <button onClick={() => setShowModal(false)}>close modal</button> </div> </React.Fragment> )} </React;Fragment> ). } ReactDOM,render( <Modal />. document;getElementById("react") )
.modal-backdrop { position: absolute; top: 0; left: 0; background: #252424cc; height: 100%; width: 100vw; }.modal { position: relative; width: 70%; background-color: white; border-radius: 10px; padding: 20px; margin:20px auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="react"></div>
有关工作示例,请参阅随附的Codesandbox 。
你快到了。 首先,您需要在handleClick()
中执行回调函数,该函数将向文档添加closeMenu
方法:
handleClick = event => {
event.preventDefault();
this.setState({ showModal: true }, () => {
document.addEventListener("click", this.closeMenu);
});
};
然后在closeMenu()
切换状态:
closeMenu = () => {
this.setState({ menuOpen: false }, () => {
document.removeEventListener('click', this.closeMenu);
});
}
每当您在组件外部单击时,它就会关闭它。 :)
这对我有用:
需要使用e.stopPropagation
来防止循环
handleClick = e => {
if (this.state.showModal) {
this.closeModal();
return;
}
this.setState({ showModal: true });
e.stopPropagation();
document.addEventListener("click", this.closeModal);
};
然后:
closeModal = () => {
this.setState({ showModal: false });
document.removeEventListener("click", this.closeModal);
};
希望会有所帮助
我是这样解决的:顺便说一句,我是初级开发人员,所以检查一下,GL。
在 index.html 中:
<div id="root"></div>
<div id="modal-root"></div>
在 index.js 中:
ReactDOM.render(
<React.StrictMode>
<ModalBase />
</React.StrictMode>,
document.getElementById("modal-root")
);
在 App.js 中:
const [showModal, setShowModal] = useState(false);
{showModal && (
<ModalBase setShowModal={setShowModal}>
{/*Your modal goes here*/}
<YourModal setShowModal={setShowModal} />
</ModalBase>
在模态容器中:
import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
const modalRoot: HTMLElement | null = document.getElementById("modal-root");
const Modal: React.FC<{
children: React.ReactNode;
setShowModal: React.Dispatch<boolean>;
}> = ({ children, setShowModal }) => {
const [el] = useState(document.createElement("div"));
const outClick = useRef(el);
useEffect(() => {
const handleOutsideClick = (
e: React.MouseEvent<HTMLDivElement, MouseEvent> | MouseEvent
) => {
const { current } = outClick;
console.log(current.childNodes[0], e.target);
if (current.childNodes[0] === e.target) {
setShowModal(false);
}
};
if (modalRoot) {
modalRoot.appendChild(el);
outClick.current?.addEventListener(
"click",
(e) => handleOutsideClick(e),
false
);
}
return () => {
if (modalRoot) {
modalRoot.removeChild(el);
el.removeEventListener("click", (e) => handleOutsideClick(e), false);
}
};
}, [el, setShowModal]);
return ReactDOM.createPortal(children, el);
};
export default Modal;
最简单的方法是在包装器中调用 closeModal 函数并在实际模态中停止传播
例如
<ModalWrapper onClick={closeModal} >
<InnerModal onClick={e => e.stopPropagation()} />
</ModalWrapper>
使用下面的onClick方法,
<div className='modal-backdrop' onClick={(e) => {
if (e.target.className === 'modal-backdrop') {
setShowModal(false)
}
}}></div>
<div className="modal">
<div>I'm a modal!</div>
<button onClick={() => setShowModal(false)}>close modal</button>
</div>
</div>
.modal-backdrop {
position: absolute;
top: 0;
left: 0;
background: #252424cc;
height: 100%;
width: 100vw;
}
您可以检查 event.target.className 是否包含父 class 您可以按如下方式关闭模态,以防您在弹出div内单击它不会关闭:
handleClick = () => {
if (e.target.className === "PARENT_CLASS") {
this.setState(prevState => ({
showModal: false
}));
}
// You should use e.stopPropagation to prevent looping
e.stopPropagation();
};
这对我有用:
const [showModal, setShowModal] = React.useState(false)
React.useEffect(() => {
document.body.addEventListener('click', () => {
setShowModal(false)
})
})
return <>
<Modal
style={{ display: showModal ? 'block' : 'none'}}
onClick={(e) => e.stopPropagation()}
/>
<button onClick={(e) => {
e.stopPropagation()
setShowModal(true)
}}>Show Modal</button>
</>
我将用功能组件来解释:
首先创建 ref 以获取对modal
元素的引用
import { useEffect, useState, useRef } from "react"; const [isModalOpen,setIsModalOpen]=useState(false) const modalEl = useRef(); <div className="modal" ref={modalEl} > I'm a modal. <button onClick={() => this.handleClick()}>close modal</button> </div>
第二个在useEffect
中创建一个事件处理程序来检测模态元素之外的事件。 为此,我们需要在元素上实现capture phase
。 (这里解释一下: 什么是事件冒泡和捕获? )。 基本上,我们将注册一个事件处理程序,以便当浏览器检测到任何事件时,浏览器将开始从顶级父 HTML 元素中查找事件处理程序,如果找到,它将调用它。
useEffect(() => { const handler = (event) => { if (.modalEl;current) { return. } // if click was not inside of the element, "." means not // in other words. if click is outside the modal element if (.modalEl;current;contains(event.target)) { setIsModalOpen(false), } }, // the key is using the `true` option // `true` will enable the `capture` phase of event handling by browser document;addEventListener("click". handler, true); return () => { document;removeEventListener("click", handler); }; }, []);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.