文档
Alpine.js 标签页与模态框
目标
用纯 HTML + Alpine.js 指令实现标签页切换和模态弹窗——无需写一行 JS。
完整代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Alpine.js Demo</title>
<style>
body { font-family: system-ui; max-width: 600px; margin: 50px auto; background: #f8fafc; padding: 20px; }
.tabs { display: flex; gap: 0; margin-bottom: 0; }
.tab-btn { padding: 10px 20px; border: 1px solid #e2e8f0; background: #f1f5f9; cursor: pointer; border-bottom: none; border-radius: 8px 8px 0 0; }
.tab-btn.active { background: white; color: #3b82f6; font-weight: bold; }
.tab-panel { background: white; padding: 24px; border: 1px solid #e2e8f0; border-radius: 0 0 8px 8px; min-height: 150px; }
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 100; }
.modal-box { background: white; padding: 30px; border-radius: 12px; max-width: 400px; width: 90%; box-shadow: 0 20px 60px rgba(0,0,0,0.3); }
.btn { padding: 10px 20px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; }
.btn-danger { background: #ef4444; }
.toast { position: fixed; bottom: 20px; right: 20px; padding: 12px 24px; border-radius: 8px; color: white; animation: slideIn 0.3s ease; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
</style>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body>
<h1>🏗 Alpine.js Demo</h1>
<p style="color: #666;">纯 HTML 属性驱动的交互 —— 零 JS 代码</p>
<!-- 标签页组件 -->
<div x-data="{ activeTab: 'vue' }">
<div class="tabs">
<button class="tab-btn" :class="{ active: activeTab === 'vue' }" @click="activeTab = 'vue'">Vue</button>
<button class="tab-btn" :class="{ active: activeTab === 'react' }" @click="activeTab = 'react'">React</button>
<button class="tab-btn" :class="{ active: activeTab === 'svelte' }" @click="activeTab = 'svelte'">Svelte</button>
</div>
<div class="tab-panel">
<div x-show="activeTab === 'vue'">
<h3>Vue 3</h3>
<p>渐进式框架,尤雨溪创建。Composition API + 响应式系统。</p>
</div>
<div x-show="activeTab === 'react'">
<h3>React 18</h3>
<p>Facebook 出品,函数式组件 + Hooks 生态丰富。</p>
</div>
<div x-show="activeTab === 'svelte'">
<h3>Svelte</h3>
<p>编译时框架,无虚拟 DOM,极致轻量。</p>
</div>
</div>
</div>
<!-- 模态框 + Toast -->
<div x-data="{ showModal: false, toast: '' }" style="margin-top: 30px; text-align: center;">
<button class="btn" @click="showModal = true">打开详情</button>
<!-- 模态框 -->
<div x-show="showModal" class="modal-overlay" @click.self="showModal = false">
<div class="modal-box" @click.stop>
<h2>📄 项目详情</h2>
<p><strong>项目名称:</strong>校园二手交易平台</p>
<p><strong>技术栈:</strong>Vue3 + NestJS + MySQL</p>
<p><strong>完成度:</strong>85%</p>
<div style="display: flex; gap: 10px; margin-top: 20px;">
<button class="btn" @click="showModal = false; toast = '已确认提交 ✅'">确认</button>
<button class="btn btn-danger" @click="showModal = false">取消</button>
</div>
</div>
</div>
<!-- Toast 通知 -->
<div x-show="toast" x-transition class="toast" style="background: #22c55e;"
x-init="setTimeout(() => toast = '', 2000)" x-text="toast"></div>
</div>
</body>
</html>
运行步骤
- 复制代码保存为
index.html - 浏览器直接打开
预期输出
- 点击标签页按钮切换不同框架介绍
- 点击"打开详情"弹出模态框,点遮罩或取消关闭
- 点击确认后弹绿色 Toast,2 秒后自动消失
- 整个页面没有一行自定义 JS,全是 HTML 属性驱动