【问题标题】:React close modal on click outside在外部点击时反应关闭模式
【发布时间】:2022-04-12 09:38:22
【问题描述】:

我已经使用没有任何库的 react 创建了一个基本模态,并且可以完美运行,现在当我单击模态外部时想要关闭模态

这里是CodeSandbox 实时预览

我的 index.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"));

【问题讨论】:

  • 请在此处添加相关代码sn-ps,而不是仅仅链接到第三方网站

标签: javascript reactjs


【解决方案1】:

最简单的方法是在包装器中调用 closeModal 函数并在实际模态中停止传播

例如

<ModalWrapper onClick={closeModal} >
  <InnerModal onClick={e => e.stopPropagation()} /> 
</ModalWrapper>

【讨论】:

  • 感谢它的工作原理!
【解决方案2】:

使用ref,会有点麻烦

观看此CodeSandBox

【讨论】:

    【解决方案3】:

    有关工作示例,请参阅附件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);
        });
      }
    

    只要您在组件外部单击,它就会关闭它。 :)

    【讨论】:

    • 不用担心。如果这对您有帮助,请投票并接受正确的答案:)
    • 但是,即使我们点击模态框,模态框也会关闭。我希望只有在外部单击时才关闭模式。 @A7DC
    【解决方案4】:

    您可以通过为与模态主体相邻的模态背景创建一个 div 来实现。使用绝对位置和 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>

    【讨论】:

      【解决方案5】:

      这对我有用:

      需要使用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);
      };
      

      希望能帮到你

      【讨论】:

      • 需要在不关闭模式的情况下点击内部。
      【解决方案6】:

      我是这样解决的: 顺便说一句,我是一名初级开发人员,请检查一下,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;
      

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2023-03-04
      • 2021-04-03
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多