效果
现代网页弹窗开发指南:从基础到优化
弹窗(Modal)作为网页交互的重要组件,在用户通知、确认操作和表单输入等场景中广泛应用。本文将循序渐进地讲解弹窗的技术实现与最佳实践。
一、弹窗基础概念
弹窗是一种覆盖在主内容之上的浮动容器,具有以下核心特征:
- 模态性:阻止用户与背景内容交互
- 聚焦性:强制用户关注弹窗内容
- 临时性:完成操作后可关闭并消失
- 层级性:位于页面其他元素之上
常见应用场景包括:确认对话框、表单提交、通知提示、图片预览和登录注册等。
二、基础弹窗实现
HTML 结构
<!-- 弹窗容器 -->
<div class="modal" id="basic-modal">
<!-- 遮罩层 -->
<div class="modal-overlay" data-close></div>
<!-- 弹窗内容 -->
<div class="modal-container">
<div class="modal-header">
<h2>弹窗标题</h2>
<button class="modal-close" data-close>×</button>
</div>
<div class="modal-body">
<p>弹窗内容区域</p>
</div>
<div class="modal-footer">
<button class="modal-btn" data-close>关闭</button>
</div>
</div>
</div>
CSS 核心样式
/* 基础样式 */
.modal {
position: fixed;
inset: 0;
z-index: 1000;
display: none;
}
/* 遮罩层 */
.modal-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.5);
}
/* 弹窗容器 */
.modal-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
border-radius: 8px;
min-width: 300px;
max-width: 90%;
}
JavaScript 控制逻辑
// 打开弹窗
function openModal(modalId) {
const modal = document.getElementById(modalId);
modal.style.display = 'block';
document.body.style.overflow = 'hidden'; // 禁止背景滚动
}
// 关闭弹窗
function closeModal(modalId) {
const modal = document.getElementById(modalId);
modal.style.display = 'none';
document.body.style.overflow = ''; // 恢复背景滚动
}
// 事件绑定
document.querySelectorAll('[data-open]').forEach(btn => {
btn.addEventListener('click', () => {
openModal(btn.dataset.open);
});
});
document.querySelectorAll('[data-close]').forEach(el => {
el.addEventListener('click', () => {
const modal = el.closest('.modal');
closeModal(modal.id);
});
});
三、进阶功能实现
1. 动画效果
添加平滑过渡动画增强用户体验:
/* 动画效果 */
.modal-container {
opacity: 0;
transform: translate(-50%, -60%) scale(0.98);
transition: all 0.3s ease;
}
.modal.active .modal-container {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.modal-overlay {
opacity: 0;
transition: opacity 0.3s ease;
}
.modal.active .modal-overlay {
opacity: 1;
}
2. 键盘操作支持
// 监听ESC键关闭弹窗
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.modal.active').forEach(modal => {
closeModal(modal.id);
});
}
});
3. 响应式设计
/* 响应式调整 */
@media (max-width: 768px) {
.modal-container {
width: 95%;
max-height: 90vh;
overflow-y: auto;
}
}
四、设计最佳实践
视觉设计
- 使用阴影和层级区分弹窗与背景
- 保持一致的边框圆角和间距
- 为不同类型弹窗使用语义化色彩(成功、警告、错误)
交互体验
- 添加打开/关闭动画,避免突兀切换
- 确保关闭按钮明显可见
- 支持点击遮罩层和 ESC 键关闭
- 弹窗打开时自动聚焦到主要操作按钮
可访问性优化
- 添加适当的 ARIA 属性
- 确保键盘可导航
- 提供清晰的操作反馈
- 避免连续弹窗打断用户流程
五、常见问题解决方案
- 滚动穿透问题 :除了设置
overflow: hidden
,还需处理触摸设备的touchmove
事件。 - 弹窗堆叠管理:维护一个弹窗栈,确保正确的显示层级和关闭顺序。
- 性能优化:避免频繁创建和销毁弹窗,可采用隐藏/显示模式复用弹窗。
- 移动端适配:优化小屏幕上的布局,确保按钮足够大以支持触摸操作。
通过合理的实现和优化,弹窗可以成为提升用户体验的有效工具,而非干扰因素。记住,好的弹窗应该是必要的、简洁的和用户友好的。
源码
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义弹窗组件示例</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#6B7280',
success: '#36D399',
warning: '#FFAB00',
danger: '#F43F5E',
},
fontFamily: {
inter: ['Inter', 'sans-serif'],
},
},
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.modal-backdrop {
backdrop-filter: blur(4px);
}
.modal-transition {
transition: opacity 0.3s ease, transform 0.3s ease;
}
.btn-shadow {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.modal-container-center {
@apply fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2;
}
}
</style>
</head>
<body class="font-inter bg-gray-50 min-h-screen flex flex-col">
<!-- 导航栏 -->
<nav class="bg-white shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<span class="text-primary font-bold text-xl">ModalDemo</span>
</div>
<div class="flex items-center space-x-4">
<button class="px-3 py-2 rounded-md text-sm font-medium text-gray-600 hover:text-primary transition-colors duration-200">
<i class="fa fa-github mr-1"></i> 源码
</button>
<button class="px-3 py-2 rounded-md text-sm font-medium text-white bg-primary hover:bg-primary/90 transition-colors duration-200 btn-shadow">
<i class="fa fa-download mr-1"></i> 下载
</button>
</div>
</div>
</div>
</nav>
<!-- 主内容区 -->
<main class="flex-grow flex flex-col items-center justify-center p-4 sm:p-6 lg:p-8">
<div class="max-w-4xl w-full bg-white rounded-xl shadow-md p-6 sm:p-8 mb-8">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-gray-800 mb-4">自定义弹窗组件示例</h1>
<p class="text-gray-600 mb-8">探索使用纯 JavaScript 实现的各种弹窗样式和交互效果。点击下方按钮查看不同类型的模态框。</p>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 sm:gap-6">
<button class="modal-trigger w-full px-4 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-basic">
<i class="fa fa-window-maximize mr-2"></i> 基础弹窗
</button>
<button class="modal-trigger w-full px-4 py-3 bg-secondary text-white rounded-lg hover:bg-secondary/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-info">
<i class="fa fa-info-circle mr-2"></i> 信息弹窗
</button>
<button class="modal-trigger w-full px-4 py-3 bg-success text-white rounded-lg hover:bg-success/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-success">
<i class="fa fa-check-circle mr-2"></i> 成功弹窗
</button>
<button class="modal-trigger w-full px-4 py-3 bg-warning text-white rounded-lg hover:bg-warning/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-warning">
<i class="fa fa-exclamation-triangle mr-2"></i> 警告弹窗
</button>
<button class="modal-trigger w-full px-4 py-3 bg-danger text-white rounded-lg hover:bg-danger/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-danger">
<i class="fa fa-times-circle mr-2"></i> 错误弹窗
</button>
<button class="modal-trigger w-full px-4 py-3 bg-gray-700 text-white rounded-lg hover:bg-gray-800 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-confirm">
<i class="fa fa-question-circle mr-2"></i> 确认弹窗
</button>
</div>
</div>
<div class="max-w-4xl w-full bg-white rounded-xl shadow-md p-6 sm:p-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">表单弹窗示例</h2>
<p class="text-gray-600 mb-6">自定义弹窗组件也可以轻松集成表单功能。</p>
<button class="modal-trigger px-5 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-all duration-200 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center" data-target="modal-form">
<i class="fa fa-user-plus mr-2"></i> 打开表单弹窗
</button>
</div>
</main>
<!-- 页脚 -->
<footer class="bg-gray-800 text-white py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<span class="text-xl font-bold">ModalDemo</span>
<p class="text-gray-400 mt-1">使用纯 JavaScript 创建的精美弹窗</p>
</div>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-white transition-colors duration-200">
<i class="fa fa-github text-xl"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors duration-200">
<i class="fa fa-twitter text-xl"></i>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors duration-200">
<i class="fa fa-linkedin text-xl"></i>
</a>
</div>
</div>
<div class="border-t border-gray-700 mt-6 pt-6 text-center text-gray-400 text-sm">
© 2025 ModalDemo. 保留所有权利。
</div>
</div>
</footer>
<!-- 基础弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-basic">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-basic-container">
<div class="modal-header bg-gray-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-gray-800">
<i class="fa fa-window-maximize text-primary mr-2"></i>基础弹窗
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-basic">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<p>这是一个基本的弹窗示例,展示了纯 JavaScript 实现的弹窗功能。</p>
<p class="mt-2">你可以自定义弹窗的内容、样式和交互逻辑。</p>
</div>
<div class="modal-footer px-6 py-4 bg-gray-50 flex justify-end space-x-3">
<button class="px-4 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors duration-200 close-modal" data-target="modal-basic">
取消
</button>
<button class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors duration-200 close-modal" data-target="modal-basic">
确认
</button>
</div>
</div>
</div>
<!-- 信息弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-info">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-info-container">
<div class="modal-header bg-blue-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-blue-800">
<i class="fa fa-info-circle text-blue-500 mr-2"></i>信息提示
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-info">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<div class="flex items-start">
<div class="flex-shrink-0 mt-0.5">
<i class="fa fa-info-circle text-blue-500 text-3xl"></i>
</div>
<div class="ml-4">
<p class="text-gray-700">这是一个信息提示弹窗,用于向用户展示重要信息。</p>
<p class="mt-2 text-gray-600">信息弹窗通常用于展示帮助文档、使用指南或系统通知等内容。</p>
</div>
</div>
</div>
<div class="modal-footer px-6 py-4 bg-blue-50 flex justify-end">
<button class="px-4 py-2 bg-blue-100 text-blue-800 rounded-lg hover:bg-blue-200 transition-colors duration-200 close-modal" data-target="modal-info">
我知道了
</button>
</div>
</div>
</div>
<!-- 成功弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-success">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-success-container">
<div class="modal-header bg-green-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-green-800">
<i class="fa fa-check-circle text-green-500 mr-2"></i>操作成功
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-success">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<div class="flex items-start">
<div class="flex-shrink-0 mt-0.5">
<i class="fa fa-check-circle text-green-500 text-3xl"></i>
</div>
<div class="ml-4">
<p class="text-gray-700">恭喜!你的操作已成功完成。</p>
<p class="mt-2 text-gray-600">你可以继续其他操作,或返回上一页。</p>
</div>
</div>
</div>
<div class="modal-footer px-6 py-4 bg-green-50 flex justify-end">
<button class="px-4 py-2 bg-green-100 text-green-800 rounded-lg hover:bg-green-200 transition-colors duration-200 close-modal" data-target="modal-success">
完成
</button>
</div>
</div>
</div>
<!-- 警告弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-warning">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-warning-container">
<div class="modal-header bg-yellow-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-yellow-800">
<i class="fa fa-exclamation-triangle text-yellow-500 mr-2"></i>警告提示
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-warning">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<div class="flex items-start">
<div class="flex-shrink-0 mt-0.5">
<i class="fa fa-exclamation-triangle text-yellow-500 text-3xl"></i>
</div>
<div class="ml-4">
<p class="text-gray-700">注意!此操作可能会产生不可预期的结果。</p>
<p class="mt-2 text-gray-600">请确认你已经了解相关风险,并谨慎操作。</p>
</div>
</div>
</div>
<div class="modal-footer px-6 py-4 bg-yellow-50 flex justify-end space-x-3">
<button class="px-4 py-2 bg-yellow-100 text-yellow-800 rounded-lg hover:bg-yellow-200 transition-colors duration-200 close-modal" data-target="modal-warning">
取消
</button>
<button class="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 transition-colors duration-200 close-modal" data-target="modal-warning">
继续
</button>
</div>
</div>
</div>
<!-- 错误弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-danger">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-danger-container">
<div class="modal-header bg-red-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-red-800">
<i class="fa fa-times-circle text-red-500 mr-2"></i>操作失败
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-danger">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<div class="flex items-start">
<div class="flex-shrink-0 mt-0.5">
<i class="fa fa-times-circle text-red-500 text-3xl"></i>
</div>
<div class="ml-4">
<p class="text-gray-700">抱歉,你的操作未能成功完成。</p>
<p class="mt-2 text-gray-600">错误信息:网络连接超时,请检查你的网络设置并重试。</p>
</div>
</div>
</div>
<div class="modal-footer px-6 py-4 bg-red-50 flex justify-end space-x-3">
<button class="px-4 py-2 bg-red-100 text-red-800 rounded-lg hover:bg-red-200 transition-colors duration-200 close-modal" data-target="modal-danger">
关闭
</button>
<button class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors duration-200 close-modal" data-target="modal-danger">
重试
</button>
</div>
</div>
</div>
<!-- 确认弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-confirm">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-md w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-confirm-container">
<div class="modal-header bg-gray-50 px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-gray-800">
<i class="fa fa-question-circle text-gray-500 mr-2"></i>确认操作
</h2>
<button class="text-gray-500 hover:text-gray-700 transition-colors duration-200 close-modal" data-target="modal-confirm">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<div class="flex items-start">
<div class="flex-shrink-0 mt-0.5">
<i class="fa fa-question-circle text-gray-500 text-3xl"></i>
</div>
<div class="ml-4">
<p class="text-gray-700">你确定要执行此操作吗?此操作无法撤销。</p>
<p class="mt-2 text-gray-600">请确认你已经了解相关风险,并谨慎操作。</p>
</div>
</div>
</div>
<div class="modal-footer px-6 py-4 bg-gray-50 flex justify-end space-x-3">
<button class="px-4 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors duration-200 close-modal" data-target="modal-confirm">
取消
</button>
<button class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors duration-200 close-modal" data-target="modal-confirm">
确认删除
</button>
</div>
</div>
</div>
<!-- 表单弹窗 -->
<div class="modal fixed inset-0 z-50 hidden" id="modal-form">
<div class="absolute inset-0 bg-black/50 modal-backdrop" data-close="true"></div>
<div class="modal-container max-w-lg w-full mx-4 bg-white rounded-xl shadow-2xl overflow-hidden modal-container-center transform transition-all duration-300 scale-95 opacity-0" id="modal-form-container">
<div class="modal-header bg-primary px-6 py-4 flex justify-between items-center">
<h2 class="text-xl font-bold text-white">
<i class="fa fa-user-plus mr-2"></i>创建新用户
</h2>
<button class="text-white hover:text-gray-200 transition-colors duration-200 close-modal" data-target="modal-form">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="modal-content px-6 py-4 text-gray-700">
<form id="user-form" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="first-name" class="block text-sm font-medium text-gray-700 mb-1">姓氏</label>
<input type="text" id="first-name" name="first-name" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors duration-200" placeholder="请输入姓氏">
</div>
<div>
<label for="last-name" class="block text-sm font-medium text-gray-700 mb-1">名字</label>
<input type="text" id="last-name" name="last-name" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors duration-200" placeholder="请输入名字">
</div>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">电子邮箱</label>
<input type="email" id="email" name="email" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors duration-200" placeholder="请输入电子邮箱">
</div>
<div>
<label for="role" class="block text-sm font-medium text-gray-700 mb-1">用户角色</label>
<select id="role" name="role" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors duration-200">
<option value="">请选择用户角色</option>
<option value="admin">管理员</option>
<option value="editor">编辑</option>
<option value="user">普通用户</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">用户权限</label>
<div class="grid grid-cols-2 gap-2">
<label class="flex items-center space-x-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50 transition-colors duration-200">
<input type="checkbox" name="permissions[]" value="view">
<span>查看权限</span>
</label>
<label class="flex items-center space-x-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50 transition-colors duration-200">
<input type="checkbox" name="permissions[]" value="edit">
<span>编辑权限</span>
</label>
<label class="flex items-center space-x-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50 transition-colors duration-200">
<input type="checkbox" name="permissions[]" value="delete">
<span>删除权限</span>
</label>
<label class="flex items-center space-x-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50 transition-colors duration-200">
<input type="checkbox" name="permissions[]" value="admin">
<span>管理权限</span>
</label>
</div>
</div>
<div>
<label for="notes" class="block text-sm font-medium text-gray-700 mb-1">备注信息</label>
<textarea id="notes" name="notes" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors duration-200" placeholder="请输入备注信息(选填)"></textarea>
</div>
</form>
</div>
<div class="modal-footer px-6 py-4 bg-gray-50 flex justify-end space-x-3">
<button class="px-4 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors duration-200 close-modal" data-target="modal-form">
取消
</button>
<button class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors duration-200" id="submit-form">
创建用户
</button>
</div>
</div>
</div>
<script>
// 打开弹窗函数
function openModal(modalId) {
const modal = document.getElementById(modalId);
const container = document.getElementById(`${modalId}-container`);
if (modal && container) {
// 显示弹窗
modal.classList.remove('hidden');
// 添加动画效果 - 延迟 10ms 以确保过渡效果生效
setTimeout(() => {
container.classList.remove('scale-95', 'opacity-0');
container.classList.add('scale-100', 'opacity-100');
}, 10);
// 阻止背景滚动
document.body.style.overflow = 'hidden';
}
}
// 关闭弹窗函数
function closeModal(modalId) {
const modal = document.getElementById(modalId);
const container = document.getElementById(`${modalId}-container`);
if (modal && container) {
// 添加关闭动画
container.classList.remove('scale-100', 'opacity-100');
container.classList.add('scale-95', 'opacity-0');
// 延迟隐藏弹窗以等待动画完成
setTimeout(() => {
modal.classList.add('hidden');
}, 300);
// 恢复背景滚动
document.body.style.overflow = '';
}
}
// 绑定打开弹窗事件
document.querySelectorAll('.modal-trigger').forEach(button => {
button.addEventListener('click', function() {
const targetModal = this.getAttribute('data-target');
openModal(targetModal);
// 添加按钮动画效果
this.classList.add('scale-95');
setTimeout(() => {
this.classList.remove('scale-95');
}, 150);
});
});
// 绑定关闭弹窗事件
document.querySelectorAll('.close-modal').forEach(button => {
button.addEventListener('click', function() {
const targetModal = this.getAttribute('data-target');
closeModal(targetModal);
// 添加按钮动画效果
this.classList.add('scale-95');
setTimeout(() => {
this.classList.remove('scale-95');
}, 150);
});
});
// 点击遮罩层关闭弹窗
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
backdrop.addEventListener('click', function() {
if (this.getAttribute('data-close') === 'true') {
const modal = this.closest('.modal');
const modalId = modal.id;
closeModal(modalId);
}
});
});
// 表单提交处理
document.getElementById('submit-form').addEventListener('click', function() {
const form = document.getElementById('user-form');
const formData = new FormData(form);
const userData = {};
// 收集表单数据
formData.forEach((value, key) => {
if (userData[key]) {
if (!Array.isArray(userData[key])) {
userData[key] = [userData[key]];
}
userData[key].push(value);
} else {
userData[key] = value;
}
});
// 模拟表单提交
console.log('提交用户数据:', userData);
// 关闭表单弹窗
closeModal('modal-form');
// 显示成功消息
setTimeout(() => {
openModal('modal-success');
}, 300);
// 重置表单
form.reset();
});
</script>
</body>
</html>