手写弹窗组件 (JavaScript 和 React 实现)

弹窗组件

弹窗组件是web项目中最常见最基础的组件,最近面试的过程中,很多应试人员都是工作中都是调用现有的UI组件库,当进一步问如何自己封装一个弹窗组件时,很多人就语塞了。所以我今天希望能从原理层面介绍并实现一个弹窗组件,并且最后封装成一个React弹窗组件,以供复用。

效果展示

在这里插入图片描述

原理介绍
 web弹窗的实现方式很多,这里介绍最简单的一种方式。弹框其实就是在主页面上显示出一层内容,我们可以通过z-index属性来控制弹窗能够显示在页面的最高层,通过display:block 和 display:none 来控制弹窗是显示隐藏。

在这里插入图片描述

代码实现
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>乐闻弹窗--dialog</title>

    <style>
        .main-container {
            width: 100%;
            height: 100vh;
        }

        .dialog-container {
            border: 1px solid #dddddd;
            background: #ffffff;
            min-width: 40%;
            min-height: 40%;
            height: 50%;
            padding: 20px;
            position: fixed;
            box-shadow: 2px 2px rgb(0, 0, 0, .1);
            top: 50%;
            left: 50%;
            transform: translateX(-50%) translateY(-50%);
            opacity: 0;
            display: none;
            transition: all 2s linear;
        }

        .dialog-container-active {
            opacity: 1;
            display: block;
            z-index: 9999;
        }

        .dialog-header {
            font-size: 2em;
            height: 20%;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
        }

        .dialog-content {
            padding: 10px 0;
            height: 60%;
            text-align: center;
        }

        .dialog-footer {
            height: 20%;
            display: flex;
            justify-content: flex-end;
            padding: 10px 0;
        }

        .dialog-footer button {
            height: 60%;
            margin-left: 20px;
            border: none;
            font-size: 1.2em;
        }

        .mask {
            position: absolute;
            right: 0;
            left: 0;
            top: 0;
            bottom: 0;
            background: rgba(0, 0, 0, .1);
            z-index: 1031;
            display: block;
        }
    </style>
</head>

<body>
    <main class="main-container" style="text-align: center;">
        <button id="dialog-btn">弹窗</button>
    </main>
    <div id="dialog" class="dialog-container">
        <div class="dialog-header">
            <div class="dialog-title">标题</div>
            <div class="dialog-close" onclick="dialogClose()">x</div>
        </div>
        <div class="dialog-content">
            内容
        </div>
        <div class="dialog-footer">
            <button onclick="dialogClose()">取消</button>
            <button onclick="alert('点击了确定按钮')">确认</button>
        </div>
    </div>
</body>

<script>
    window.onload = function () {
        let dialogBtn = document.getElementById('dialog-btn');
        dialogBtn.addEventListener('click', function () {
            let dialog = document.getElementById('dialog');
            dialog.classList.add('dialog-container-active')

            let mask = document.createElement('div');
            mask.setAttribute('id', 'mask')
            mask.setAttribute('class', 'mask')
            mask.setAttribute('onclick', 'dialogClose()')
            document.body.appendChild(mask)
        })
    }

    function dialogClose() {
        let mask = document.getElementById('mask');
        document.body.removeChild(mask);//删除遮罩层
        let dialog = document.getElementById('dialog');
        dialog.classList.remove('dialog-container-active')
    }
</script>

</html>

React封装弹窗

这里补充「React封装弹窗」,项目基于 create-react-app 脚手架生成

效果展示

在这里插入图片描述

项目结构

在这里插入图片描述

核心代码
// components/Modal/index.jsx
import React, { ReactNode } from "react";
import classnames from "classnames";
import "./index.css";

export default function Modal(props) {
  const { visible, onClose, title, children } = props;
  return (
    <div className={classnames("modal", { "modal-visible": visible })}>
      {/* 弹窗内容部分 */}
      <div className="modal-body">
        <div className="modal-body__title">
          <span>{title}</span>
          <span onClick={onClose}>x</span>
        </div>
        <div className="modal-body__content">{children}</div>
        <div className="modal-body__footer">
          <button className="cancel-btn" onClick={onClose}>
            取消
          </button>
          <button className="ensure-btn">确定</button>
        </div>
      </div>
      {/* 遮罩 */}
      <div className="modal-mask"></div>
    </div>
  );
component/Modal/index.css
.modal {
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  transform: scale(0);
  transition: all 0.3s ease-in-out;
}

.modal-visible {
  transform: scale(1);
}

.modal-body {
  position: relative;
  width: 500px;
  height: 500px;
  background: #fff;
  border-radius: 4px;
  padding: 12px;
  z-index: 99;
}

.modal-body__title {
  padding: 8px 0;
  font-size: 18px;
  font-weight: 800px;
  color: #333;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.modal-body__content {
  padding: 8px 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-body__footer {
  position: absolute;
  bottom: 12px;
  right: 12px;
  display: flex;
  gap: 8px;
  align-items: center;
  flex-direction: row-reverse;
}

.cancel-btn {
  background-color: rgb(170, 170, 170);
}

.ensure-btn {
  background-color: rgb(78, 78, 243);
}

.modal-mask {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.3);
}
// App.js
import React, { useState } from 'react';
import './App.css';
import Modal from './components/Modal';

function App() {
  const [visible, setVisible] = useState();
  return (
    <div className="app-body">
      <button className="ensure-btn" onClick={() => setVisible(true)}>打开弹窗</button>

      {/* 弹窗组件 */}
      <Modal
        visible={visible}
        title={'弹窗标题'}
        onClose={() => setVisible(false)} >
        弹窗内容
      </Modal>
    </div>
  );
}

export default App;

// App.css
.app-body {
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
}

.app-body button {
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  color: #fff;
  cursor: pointer;
}

源码地址

dumbo-modal

总结

弹窗组件的实现有很多,但是原理大致相同,激活弹窗时把弹窗的z-index 提高,并显示隐藏的弹窗dom结构。
后续会补充一些显示动效,让弹窗变得更炫酷。有问题或遗漏请帮忙指出,谢谢。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐闻x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值