<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>子HTML弹窗 与 弹框 传参demo(单文件)</title>
<style>
:root{
--primary:#4f6ef7;
--primary-dark:#3b55d9;
--bg:#f4f6fb;
--card:#ffffff;
--text:#1f2430;
--text-secondary:#6b7280;
--border:#e5e7eb;
--success-bg:#eafaf0;
--success-text:#1a7f4e;
--radius:14px;
}
*{box-sizing:border-box;}
body{
margin:0;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
background:var(--bg);
color:var(--text);
padding:40px 20px;
}
.container{max-width:760px;margin:0 auto;}
h1{font-size:24px;font-weight:600;margin:0 0 6px;}
.subtitle{color:var(--text-secondary);font-size:14px;margin:0 0 32px;}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;}
@media(max-width:640px){.grid{grid-template-columns:1fr;}}
.card{
background:var(--card);
border:1px solid var(--border);
border-radius:var(--radius);
padding:24px;
display:flex;
flex-direction:column;
gap:14px;
box-shadow:0 1px 3px rgba(0,0,0,0.04);
}
.card h2{font-size:16px;margin:0;display:flex;align-items:center;gap:8px;}
.card p{font-size:13px;color:var(--text-secondary);margin:0;line-height:1.6;flex:1;}
.badge{
display:inline-block;
font-size:11px;
padding:3px 8px;
border-radius:999px;
background:#eef1ff;
color:var(--primary-dark);
width:fit-content;
}
button.action{
background:var(--primary);
color:#fff;
border:none;
border-radius:10px;
padding:10px 16px;
font-size:14px;
cursor:pointer;
transition:background .15s ease, transform .1s ease;
}
button.action:hover{background:var(--primary-dark);}
button.action:active{transform:scale(0.98);}
.result-box{
margin-top:28px;
background:var(--card);
border:1px solid var(--border);
border-radius:var(--radius);
padding:20px 24px;
}
.result-box h3{font-size:14px;margin:0 0 12px;color:var(--text-secondary);font-weight:500;}
.result-item{
display:none;
align-items:center;
gap:10px;
background:var(--success-bg);
color:var(--success-text);
padding:10px 14px;
border-radius:10px;
font-size:13px;
margin-bottom:8px;
}
.result-item.show{display:flex;}
.result-empty{font-size:13px;color:var(--text-secondary);}
/* ---- 弹框(模态对话框)样式 ---- */
dialog{
border:none;
border-radius:16px;
padding:0;
width:min(380px,92vw);
box-shadow:0 20px 60px rgba(0,0,0,0.18);
}
dialog::backdrop{background:rgba(20,20,30,0.35);}
.modal-inner{padding:24px;}
.modal-inner h3{margin:0 0 4px;font-size:17px;}
.modal-inner .modal-sub{font-size:13px;color:var(--text-secondary);margin:0 0 18px;}
.field{margin-bottom:14px;}
.field label{display:block;font-size:12px;color:var(--text-secondary);margin-bottom:6px;}
.field input{
width:100%;
padding:9px 12px;
border:1px solid var(--border);
border-radius:8px;
font-size:14px;
outline:none;
}
.field input:focus{border-color:var(--primary);}
.modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px;}
button.ghost{
background:transparent;
border:1px solid var(--border);
border-radius:10px;
padding:9px 16px;
font-size:14px;
cursor:pointer;
color:var(--text);
}
button.ghost:hover{background:#f3f4f6;}
/* ---- 代码片段展示 ---- */
pre.code{
background:#1e2230;
color:#e3e6f0;
border-radius:10px;
padding:14px 16px;
font-size:12.5px;
line-height:1.6;
overflow-x:auto;
margin:0;
font-family:"SFMono-Regular",Consolas,"Courier New",monospace;
}
pre.code .kw{color:#7aa2f7;}
pre.code .fn{color:#c0caf5;}
pre.code .str{color:#9ece6a;}
pre.code .com{color:#6b7280;}
</style>
</head>
<body>
<div class="container">
<h1>主页面(单文件demo)</h1>
<p class="subtitle">演示两种向主页面传参的方式:弹出子HTML窗口 vs 页内弹框,全部写在同一个 html 文件里</p>
<div class="grid">
<div class="card">
<span class="badge">window.open</span>
<h2>① 打开子HTML页面</h2>
<p>点击后用 Blob 动态生成一个独立子页面并以弹出窗口形式打开(不是新标签页)。在子页面填写信息提交后,通过 postMessage 把数据传回本页面,随后子窗口自动关闭。</p>
<button class="action" id="openChildBtn">打开子页面</button>
<pre class="code"><span class="com">// 子页面:打开弹出窗口</span>
<span class="kw">const</span> url = URL.createObjectURL(blob);
<span class="fn">window.open</span>(url, <span class="str">'childWindow'</span>,
<span class="str">'width=380,height=380'</span>);
<span class="com">// 子页面:把数据传回主页面</span>
window.opener.<span class="fn">postMessage</span>(
{ type: <span class="str">'CHILD_SUBMIT'</span>, payload: data },
<span class="str">'*'</span>
);
<span class="com">// 主页面:接收数据</span>
window.<span class="fn">addEventListener</span>(<span class="str">'message'</span>, e => {
<span class="kw">const</span> data = e.data.payload;
});</pre>
</div>
<div class="card">
<span class="badge"><dialog></span>
<h2>② 打开弹框</h2>
<p>点击后在当前页面内弹出模态对话框,不产生新窗口。填写并提交后直接调用主页面函数拿到数据。</p>
<button class="action" id="openModalBtn">打开弹框</button>
<pre class="code"><span class="com">// HTML:弹框本体,与主页面同属一个DOM</span>
<<span class="kw">dialog</span> id=<span class="str">"myModal"</span>>...</<span class="kw">dialog</span>>
<span class="com">// 打开弹框</span>
modal.<span class="fn">showModal</span>();
<span class="com">// 提交时直接读取同页面的表单值</span>
form.<span class="fn">addEventListener</span>(<span class="str">'submit'</span>, e => {
<span class="kw">const</span> name = nameInput.value;
<span class="com">// 无需跨窗口通信,直接使用</span>
});</pre>
</div>
</div>
<div class="result-box">
<h3>接收到的数据</h3>
<div class="result-item" id="childResult"></div>
<div class="result-item" id="modalResult"></div>
<p class="result-empty" id="emptyTip">暂无数据,试试上面两个按钮</p>
</div>
</div>
<!-- 弹框(模态对话框)本体,与主页面同属一个DOM -->
<dialog id="myModal">
<div class="modal-inner">
<h3>填写信息(弹框)</h3>
<p class="modal-sub">提交后数据直接传回主页面,不会离开当前页面</p>
<form id="modalForm" method="dialog">
<div class="field">
<label>姓名</label>
<input type="text" id="modalName" required placeholder="请输入姓名">
</div>
<div class="field">
<label>备注</label>
<input type="text" id="modalNote" placeholder="选填">
</div>
<div class="modal-actions">
<button type="button" class="ghost" id="cancelModal">取消</button>
<button type="submit" class="action">提交</button>
</div>
</form>
</div>
</dialog>
<script>
var emptyTip = document.getElementById('emptyTip');
var childResult = document.getElementById('childResult');
var modalResult = document.getElementById('modalResult');
function showResult(el, text){
el.textContent = text;
el.classList.add('show');
emptyTip.style.display = 'none';
}
// ---- ① 打开子HTML页面(弹出窗口,用 Blob 动态生成,不需要单独的 html 文件) ----
document.getElementById('openChildBtn').addEventListener('click', function(){
var childHtml = [
'<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">',
'<title>子页面</title>',
'<style>',
'body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;background:#fff;color:#1f2430;padding:22px;}',
'h2{font-size:16px;margin:0 0 4px;}',
'.sub{font-size:12px;color:#6b7280;margin:0 0 18px;}',
'.field{margin-bottom:14px;}',
'.field label{display:block;font-size:12px;color:#6b7280;margin-bottom:6px;}',
'.field input{width:100%;padding:9px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;box-sizing:border-box;outline:none;}',
'.field input:focus{border-color:#4f6ef7;}',
'.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px;}',
'button{border-radius:10px;padding:9px 16px;font-size:14px;cursor:pointer;border:1px solid #e5e7eb;background:#fff;color:#1f2430;}',
'button.primary{background:#4f6ef7;color:#fff;border:none;}',
'button.primary:hover{background:#3b55d9;}',
'button:hover{background:#f3f4f6;}',
'</style></head><body>',
'<h2>我是子HTML页面</h2>',
'<p class="sub">这是一个独立的弹出窗口,提交后会把数据传回打开它的主页面</p>',
'<form id="childForm">',
'<div class="field"><label>姓名</label><input type="text" id="name" required placeholder="请输入姓名"></div>',
'<div class="field"><label>邮箱</label><input type="email" id="email" required placeholder="请输入邮箱"></div>',
'<div class="actions"><button type="button" id="closeBtn">取消</button><button type="submit" class="primary">提交并关闭</button></div>',
'</form>',
'<script>',
'document.getElementById("closeBtn").addEventListener("click", function(){ window.close(); });',
'document.getElementById("childForm").addEventListener("submit", function(e){',
' e.preventDefault();',
' var data = { name: document.getElementById("name").value, email: document.getElementById("email").value };',
' if(window.opener){ window.opener.postMessage({ type: "CHILD_SUBMIT", payload: data }, "*"); }',
' window.close();',
'});',
'<\/script>',
'</body></html>'
].join('');
var blob = new Blob([childHtml], { type: 'text/html' });
var url = URL.createObjectURL(blob);
// width/height 等参数是让浏览器以"弹出窗口"而非"新标签页"打开的关键
window.open(url, 'childWindow', 'width=380,height=380,left=200,top=150');
});
// 监听子窗口通过 postMessage 传回的数据
window.addEventListener('message', function(event){
if(event.data && event.data.type === 'CHILD_SUBMIT'){
var d = event.data.payload;
showResult(childResult, '来自子页面: 姓名=' + d.name + ', 邮箱=' + d.email);
}
});
// ---- ② 弹框(模态对话框) ----
var modal = document.getElementById('myModal');
document.getElementById('openModalBtn').addEventListener('click', function(){
modal.showModal();
});
document.getElementById('cancelModal').addEventListener('click', function(){
modal.close();
});
document.getElementById('modalForm').addEventListener('submit', function(e){
var name = document.getElementById('modalName').value;
var note = document.getElementById('modalNote').value;
showResult(modalResult, '来自弹框: 姓名=' + name + (note ? ', 备注=' + note : ''));
document.getElementById('modalForm').reset();
});
</script>
</body>
</html>