繁体   English   中英

在外部单击时反应关闭模式

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM