【前端】【组件库开发】【原理】【无框架开发】现代网页弹窗开发指南:从基础到优化

效果



现代网页弹窗开发指南:从基础到优化

弹窗(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>&times;</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 属性
  • 确保键盘可导航
  • 提供清晰的操作反馈
  • 避免连续弹窗打断用户流程

五、常见问题解决方案

  1. 滚动穿透问题 :除了设置overflow: hidden,还需处理触摸设备的touchmove事件。
  2. 弹窗堆叠管理:维护一个弹窗栈,确保正确的显示层级和关闭顺序。
  3. 性能优化:避免频繁创建和销毁弹窗,可采用隐藏/显示模式复用弹窗。
  4. 移动端适配:优化小屏幕上的布局,确保按钮足够大以支持触摸操作。

通过合理的实现和优化,弹窗可以成为提升用户体验的有效工具,而非干扰因素。记住,好的弹窗应该是必要的、简洁的和用户友好的。

源码

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>
    
相关推荐
星语卿17 分钟前
浏览器重绘与重排
前端·浏览器
小小小小宇32 分钟前
前端实现合并两个已排序链表
前端
yngsqq1 小时前
netdxf—— CAD c#二次开发之(netDxf 处理 DXF 文件)
java·前端·c#
mrsk1 小时前
🧙‍♂️ CSS中的结界术:BFC如何拯救你的布局混乱?
前端·css·面试
jonssonyan1 小时前
我自建服务器部署了 Next.js 全栈项目
前端
A了LONE1 小时前
h5的底部导航栏模板
java·前端·javascript
专注VB编程开发20年1 小时前
各版本操作系统对.NET支持情况(250707更新)
开发语言·前端·ide·vscode·.net
Zsnoin能1 小时前
AI + TailwindCSS快速搭建一个属于自己的TailwindCSS学习网站
前端·css
五号厂房1 小时前
聊一聊Javascript 中 hasOwnProperty和in操作之间的区别
前端
摆烂为不摆烂1 小时前
😁深入JS(六): 一文让你完全理解浏览器进程与线程
前端·javascript