如何向新窗口传递函数并在父窗口获取结果后关闭子窗口?
当然可以实现这个需求!不过得根据子窗口和父窗口是否同源来选择不同的方案——毕竟浏览器的同源策略会限制不同域之间的直接交互,下面分两种场景给你详细说明:
一、同源网站(子窗口与父窗口域名完全一致)
这种情况操作起来比较省心,因为同源下父窗口可以直接访问子窗口的全局对象,甚至修改它的DOM。
实现步骤:
- 打开子窗口时保存好引用,方便后续操作
- 将父窗口的
parse函数注入到子窗口的全局作用域 - 子窗口执行函数后,把结果传回父窗口(同步或异步都可以)
- 父窗口拿到结果后,直接关闭对应的子窗口
同步解析示例代码:
const urls = ["first.com", "second.com", "third.com"]; // 你的解析函数 function parse(url) { // 这里写具体解析逻辑,比如获取页面标题、特定DOM内容等 return `解析结果:${url} - 当前页面标题:${document.title}`; } // 循环处理每个URL urls.forEach(url => { // 打开子窗口并保存引用 const childWindow = window.open(url, "_blank", "width=800,height=600"); // 等待子窗口加载完成后执行操作 childWindow.onload = function() { // 把父窗口的parse函数赋值给子窗口的全局变量 childWindow.parentParse = parse; // 在子窗口环境中执行解析函数,拿到结果 const result = childWindow.parentParse(url); // 父窗口处理结果(比如打印、存到数组里) console.log(`处理${url}完成:`, result); // 关闭子窗口 childWindow.close(); }; });
异步解析示例(比如需要请求数据):
如果你的parse是异步函数(比如包含fetch、setTimeout),可以让子窗口主动通知父窗口结果:
const urls = ["first.com", "second.com", "third.com"]; const childWindowMap = {}; // 保存窗口引用和URL的映射 // 异步解析函数 function parse(url) { return new Promise(resolve => { // 模拟异步操作,比如请求接口 setTimeout(() => { resolve(`异步解析结果:${url} - ${document.title}`); }, 1500); }); } // 父窗口接收结果的回调 function handleChildResult(url, result) { console.log(`处理${url}完成:`, result); // 关闭对应子窗口 const childWin = childWindowMap[url]; if (childWin) { childWin.close(); delete childWindowMap[url]; } } urls.forEach(url => { const childWindow = window.open(url, "_blank", "width=800,height=600"); childWindowMap[url] = childWindow; childWindow.onload = function() { // 传递函数和当前URL到子窗口 childWindow.parentParse = parse; childWindow.currentUrl = url; // 在子窗口执行异步函数 childWindow.parentParse(childWindow.currentUrl) .then(result => { // 通过window.opener通知父窗口结果 window.opener.handleChildResult(childWindow.currentUrl, result); }); }; });
二、跨域网站(子窗口与父窗口域名不同)
这种情况受限于同源策略,父窗口不能直接修改子窗口的全局对象,这时候就得用浏览器提供的postMessage API来实现跨域通信。
关键前提:
你需要可控子窗口的页面代码,因为要在子窗口中添加消息监听逻辑,接收父窗口发来的函数并返回结果。
实现步骤:
- 父窗口打开子窗口并保存引用
- 子窗口加载完成后,父窗口把
parse函数转成字符串(函数不能直接跨域传递),通过postMessage发送给子窗口 - 子窗口接收消息,把字符串还原成函数并执行
- 子窗口通过
postMessage把结果传回父窗口 - 父窗口收到结果后关闭子窗口
父窗口代码:
const urls = ["first.com", "second.com", "third.com"]; const childWindowMap = new Map(); // 你的解析函数 function parse(url) { // 注意:跨域下子窗口只能获取自己页面的内容,所以这里的逻辑是子窗口执行时的环境 return `跨域解析结果:${url} - 子窗口标题:${document.title}`; } // 监听子窗口发来的结果 window.addEventListener("message", function(event) { // 验证消息来源,避免恶意消息(建议指定具体域名,比如"https://first.com") if (!urls.includes(event.data.url)) return; const { url, result } = event.data; console.log(`处理${url}完成:`, result); // 关闭对应子窗口 const childWin = childWindowMap.get(url); if (childWin) { childWin.close(); childWindowMap.delete(url); } }); urls.forEach(url => { const childWindow = window.open(url, "_blank", "width=800,height=600"); childWindowMap.set(url, childWindow); // 等待子窗口加载完成后发送函数 childWindow.onload = function() { childWindow.postMessage({ type: "injectParseFunction", funcStr: parse.toString(), // 把函数转成字符串 url: url }, "*"); // 生产环境建议替换成子窗口的具体域名,比如"https://first.com" }; });
子窗口代码(需要添加到子窗口的页面中):
// 监听父窗口发来的消息 window.addEventListener("message", function(event) { if (event.data.type === "injectParseFunction") { const { funcStr, url } = event.data; // 把字符串还原成函数 const parse = new Function(`return ${funcStr}`)(); // 执行解析函数 const result = parse(url); // 把结果传回父窗口 event.source.postMessage({ url: url, result: result }, event.origin); // 只回传给父窗口的域名,更安全 } });
注意事项:
- 跨域场景下,
postMessage的targetOrigin尽量指定具体域名,不要用*,避免安全风险 - 如果子窗口加载时间不确定,可以让子窗口加载完成后主动给父窗口发消息,父窗口再发送函数,这样比
onload更可靠 - 若子窗口不可控(比如第三方网站),这种方案无法实现,因为没法添加监听代码
内容的提问来源于stack exchange,提问作者Ильшат Мурзурбеков




