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