dialog 对话框
<dialog> 元素用于创建原生对话框(模态或非模态),无需 JavaScript 库即可实现。它提供了 show() 和 showModal() 方法来控制对话框的显示方式,配合 ::backdrop 伪元素可以轻松实现模态遮罩效果。
前置知识
阅读本节前,建议先了解:details 与 summary 折叠
基础概念
什么是 dialog
<dialog> 是 HTML5.2 引入的语义化元素,用于表示对话框、警告框或交互式弹出窗口。它原生支持模态和非模态两种模式,比自定义的 div 弹窗更易于实现和访问。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>dialog 示例</title>
</head>
<body>
<!-- 定义对话框(默认隐藏) -->
<dialog id="myDialog">
<h2>对话框标题</h2>
<p>这是对话框内容。</p>
<button onclick="document.getElementById('myDialog').close()">关闭</button>
</dialog>
<!-- 触发按钮 -->
<button onclick="document.getElementById('myDialog').showModal()">打开对话框</button>
</body>
</html>两种显示模式
| 模式 | 方法 | 特征 |
|---|---|---|
| 非模态 | show() | 不阻塞页面交互,不显示遮罩 |
| 模态 | showModal() | 阻塞页面交互,显示遮罩,Tab 陷阱 |
语法与使用
基本语法
html
<!-- 非模态对话框 -->
<dialog id="dialog1">
<p>内容</p>
<button onclick="this.closest('dialog').close()">关闭</button>
</dialog>
<!-- 模态对话框 -->
<dialog id="dialog2">
<form method="dialog">
<p>内容</p>
<button value="confirm">确认</button>
<button value="cancel">取消</button>
</form>
</dialog>JavaScript API
javascript
const dialog = document.getElementById('myDialog');
// 打开非模态对话框
dialog.show();
// 打开模态对话框
dialog.showModal();
// 关闭对话框
dialog.close();
// 关闭并返回值
dialog.close('confirmed');
// 监听关闭事件
dialog.addEventListener('close', () => {
console.log('关闭原因:', dialog.returnValue);
});
// 监听取消事件(按 ESC)
dialog.addEventListener('cancel', (e) => {
e.preventDefault(); // 阻止默认的 ESC 关闭行为
});
// 检查对话框是否打开
console.log(dialog.open);::backdrop 伪元素
模态对话框自动创建遮罩层,可以通过 ::backdrop 自定义样式:
css
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}详细说明
确认对话框
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>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 2rem; }
/* dialog 基础样式 */
dialog {
border: none; border-radius: 12px;
padding: 0; max-width: 420px; width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
/* 非模态 dialog 默认隐藏 */
dialog:not([open]) { display: none; }
/* 模态遮罩 */
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
/* 对话框内容 */
.dialog-header {
padding: 1.5rem; border-bottom: 1px solid #e2e8f0;
}
.dialog-header h2 { font-size: 1.1rem; color: #1e293b; }
.dialog-body {
padding: 1.5rem; color: #475569; line-height: 1.6;
}
.dialog-footer {
padding: 1rem 1.5rem; border-top: 1px solid #e2e8f0;
display: flex; justify-content: flex-end; gap: 0.75rem;
}
.btn {
padding: 0.5rem 1.25rem; border-radius: 6px;
border: none; cursor: pointer; font-size: 0.9rem;
transition: all 0.2s;
}
.btn-cancel { background: #f1f5f9; color: #475569; }
.btn-cancel:hover { background: #e2e8f0; }
.btn-danger { background: #ef4444; color: #fff; }
.btn-danger:hover { background: #dc2626; }
.btn-primary { background: #3b82f6; color: #fff; }
.btn-primary:hover { background: #2563eb; }
.trigger-btn {
padding: 0.75rem 1.5rem; background: #3b82f6; color: #fff;
border: none; border-radius: 6px; cursor: pointer; font-size: 1rem;
}
</style>
</head>
<body>
<h1>确认删除</h1>
<p>以下示例展示了一个确认删除的对话框。</p>
<button class="trigger-btn" onclick="confirmDialog.showModal()">删除项目</button>
<p id="result" style="margin-top:1rem;color:#64748b"></p>
<!-- 确认对话框 -->
<dialog id="confirmDialog">
<div class="dialog-header">
<h2>确认删除</h2>
</div>
<div class="dialog-body">
<p>您确定要删除这个项目吗?此操作无法撤销,所有相关数据将被永久删除。</p>
</div>
<div class="dialog-footer">
<button class="btn btn-cancel" onclick="confirmDialog.close('cancelled')">取消</button>
<button class="btn btn-danger" onclick="confirmDialog.close('deleted')">确认删除</button>
</div>
</dialog>
<script>
const confirmDialog = document.getElementById('confirmDialog');
const result = document.getElementById('result');
confirmDialog.addEventListener('close', () => {
if (confirmDialog.returnValue === 'deleted') {
result.textContent = '项目已删除。';
result.style.color = '#ef4444';
} else {
result.textContent = '已取消操作。';
result.style.color = '#64748b';
}
});
</script>
</body>
</html>表单对话框
使用 <form method="dialog"> 可以在对话框中提交表单值:
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>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 2rem; }
dialog {
border: none; border-radius: 12px;
padding: 0; max-width: 480px; width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.form-dialog-header {
padding: 1.5rem; border-bottom: 1px solid #e2e8f0;
}
.form-dialog-header h2 { font-size: 1.1rem; color: #1e293b; }
.form-dialog-body {
padding: 1.5rem;
}
.form-group { margin-bottom: 1rem; }
.form-group label {
display: block; font-size: 0.875rem; font-weight: 500;
color: #334155; margin-bottom: 0.25rem;
}
.form-group input, .form-group select {
width: 100%; padding: 0.6rem 0.75rem;
border: 1px solid #d1d5db; border-radius: 6px;
font-size: 0.9rem; outline: none;
}
.form-group input:focus, .form-group select:focus {
border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
}
.form-dialog-footer {
padding: 1rem 1.5rem; border-top: 1px solid #e2e8f0;
display: flex; justify-content: flex-end; gap: 0.75rem;
}
.btn {
padding: 0.6rem 1.25rem; border-radius: 6px;
border: none; cursor: pointer; font-size: 0.9rem;
}
.btn-cancel { background: #f1f5f9; color: #475569; }
.btn-primary { background: #3b82f6; color: #fff; }
.trigger-btn {
padding: 0.75rem 1.5rem; background: #3b82f6; color: #fff;
border: none; border-radius: 6px; cursor: pointer;
}
</style>
</head>
<body>
<h1>用户设置</h1>
<button class="trigger-btn" onclick="settingsDialog.showModal()">编辑资料</button>
<p id="status" style="margin-top:1rem"></p>
<!-- 表单对话框 -->
<dialog id="settingsDialog">
<!-- form method="dialog" 会使表单提交时关闭对话框 -->
<form method="dialog">
<div class="form-dialog-header">
<h2>编辑个人资料</h2>
</div>
<div class="form-dialog-body">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" value="张三">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" name="email" value="zhang@example.com">
</div>
<div class="form-group">
<label for="role">角色</label>
<select id="role" name="role">
<option value="developer">开发者</option>
<option value="designer">设计师</option>
<option value="manager">管理者</option>
</select>
</div>
</div>
<div class="form-dialog-footer">
<button class="btn btn-cancel" type="submit" value="cancel">取消</button>
<button class="btn btn-primary" type="submit" value="save">保存</button>
</div>
</form>
</dialog>
<script>
const settingsDialog = document.getElementById('settingsDialog');
const status = document.getElementById('status');
settingsDialog.addEventListener('close', () => {
if (settingsDialog.returnValue === 'save') {
status.textContent = '资料已保存。';
} else {
status.textContent = '已取消编辑。';
}
});
</script>
</body>
</html>非模态对话框
html
<dialog id="tooltipDialog" style="position:absolute;top:50px;left:50px;">
<p>这是一个非模态提示框,不会阻塞页面。</p>
<button onclick="this.closest('dialog').close()">关闭</button>
</dialog>
<button onclick="document.getElementById('tooltipDialog').show()">显示提示</button>实战示例
通知弹窗组件
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>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; padding: 2rem; }
dialog {
border: none; border-radius: 12px;
padding: 0; width: 360px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
dialog::backdrop { background: rgba(0,0,0,0.3); }
.notification {
padding: 1.5rem; display: flex; gap: 1rem; align-items: flex-start;
}
.notification-icon {
width: 40px; height: 40px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 1.25rem; flex-shrink: 0;
}
.notification-success .notification-icon { background: #dcfce7; }
.notification-warning .notification-icon { background: #fef3c7; }
.notification-error .notification-icon { background: #fee2e2; }
.notification h3 { font-size: 0.95rem; margin-bottom: 0.25rem; }
.notification p { font-size: 0.85rem; color: #64748b; line-height: 1.5; }
.notification-actions { margin-top: 1rem; display: flex; justify-content: flex-end; gap: 0.5rem; }
.notification-actions button {
padding: 0.4rem 1rem; border-radius: 4px;
border: 1px solid #d1d5db; background: #fff;
cursor: pointer; font-size: 0.85rem;
}
.action-buttons { margin-top: 1.5rem; display: flex; gap: 1rem; }
.action-btn {
padding: 0.5rem 1rem; border: none; border-radius: 6px;
cursor: pointer; font-size: 0.9rem;
}
</style>
</head>
<body>
<h1>通知对话框</h1>
<div class="action-buttons">
<button class="action-btn" style="background:#dcfce7" onclick="successDialog.showModal()">成功通知</button>
<button class="action-btn" style="background:#fef3c7" onclick="warningDialog.showModal()">警告通知</button>
<button class="action-btn" style="background:#fee2e2" onclick="errorDialog.showModal()">错误通知</button>
</div>
<dialog id="successDialog">
<div class="notification notification-success">
<div class="notification-icon">✔</div>
<div>
<h3>操作成功</h3>
<p>您的文件已成功上传到服务器。</p>
<div class="notification-actions">
<button onclick="successDialog.close()">确定</button>
</div>
</div>
</div>
</dialog>
<dialog id="warningDialog">
<div class="notification notification-warning">
<div class="notification-icon">⚠</div>
<div>
<h3>存储空间不足</h3>
<p>您的存储空间已使用 95%,建议清理不需要的文件。</p>
<div class="notification-actions">
<button onclick="warningDialog.close()">稍后处理</button>
<button onclick="warningDialog.close()" style="background:#3b82f6;color:#fff;border-color:#3b82f6">立即清理</button>
</div>
</div>
</div>
</dialog>
<dialog id="errorDialog">
<div class="notification notification-error">
<div class="notification-icon">✘</div>
<div>
<h3>上传失败</h3>
<p>文件上传失败,请检查网络连接后重试。</p>
<div class="notification-actions">
<button onclick="errorDialog.close()">关闭</button>
<button onclick="errorDialog.close()" style="background:#3b82f6;color:#fff;border-color:#3b82f6">重试</button>
</div>
</div>
</div>
</dialog>
</body>
</html>注意事项
ESC 关闭行为
模态对话框默认支持 ESC 键关闭。如果需要阻止:
javascript
dialog.addEventListener('cancel', (e) => {
if (unsavedChanges) {
e.preventDefault(); // 阻止 ESC 关闭
// 显示确认对话框
}
});表单验证
模态对话框中的表单可以使用 HTML5 原生验证:
html
<dialog id="formDialog">
<form method="dialog">
<input type="text" required minlength="2">
<button type="submit" value="ok">提交</button>
</form>
</dialog>最佳实践
- 使用 showModal() 实现模态:大多数场景应使用模态对话框
- 提供关闭方式:确保对话框有关闭按钮和 ESC 键支持
- 使用 form method="dialog":简化表单对话框的关闭逻辑
- 自定义 backdrop:使用
::backdrop创建合适的遮罩效果 - 焦点管理:模态对话框自动管理焦点,非模态需要手动处理
下一节
继续学习:语义化 vs div 布局