iframe与主窗口通信
1. 引言
<iframe> 元素是 HTML 中的一个标签,用于在当前页面中嵌入另一个页面
使用 <iframe> 可以实现以下功能:
- 嵌入其他网页:可以将其他网页嵌入到当前页面中,例如显示地图、视频、文档等
- 嵌入本地页面:可以将其他页面或组件嵌入到当前页面中,以实现模块化和复用
嵌入的iframe和主网页之间具有一定的独立性,无法像正常的一个网页上下文一样访问内容,如何通信成为一个问题
本文主要记述iframe和主窗口之间的通信方式,包含内置方法和使用Postmate库
2. 概述
iframe标签在JS中定义为HTMLIFrameElement,而HTMLIFrameElement.contentWindow返回当前HTMLIFrameElement的Window对象,可以使用这个Window 对象去访问这个 iframe 的文档和它内部的 DOM
Window对象可以接收消息(onmessage - Web API 接口参考 | MDN (mozilla.org))和发送消息(window.postMessage - Web API 接口参考 | MDN (mozilla.org))
另外,Window对象还存在父对象(window.parent - Web API 接口参考 | MDN (mozilla.org)),可以通过window.parent访问父对象
利用上述的方法与特性,就可以实现iframe和主窗口之间的通讯
思路之一是:将需要对方访问的属性和方法挂载到window对象上,从而主窗口通过document.querySelector('iframe').contentWindow.<xxx>访问iframe,iframe通过window.parent.<xxx>访问主窗口的属性或方法
思路之二是:主窗口向iframe发送信息postMessage(),并监听子对象的信息,iframe监听主窗口信息并向主窗口发送信息
由于安全原因与浏览器限制,非同源URL(同地址同端口)不可以使用第一种方法,第二种方法均适用
3. 内置方法
3.1 初始准备
笔者准备了这样两个网页,分别叫parent.html和children.html
parent.html内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#parent {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
}
iframe {
width: 80%;
height: 100%;
}
#panel {
width: 20%;
height: 100%;
background: white;
display: flex;
flex-direction: column;
}
code {
font-size: large;
}
</style>
</head>
<body>
<div id="parent">
<iframe src="./children.html" frameborder="0"></iframe>
<div id="panel">
<h3>主窗口</h3>
<code></code>
</div>
</div>
<script>
</script>
</body>
</html>
children.html内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#container {
width: 100%;
height: 100%;
background: cadetblue;
display: flex;
flex-direction: column;
}
code {
font-size: large;
}
</style>
</head>
<body>
<div id="container">
<h3>子窗口</h3>
<code></code>
</div>
<script>
</script>
</body>
</html>
这两网页的界面如下:

现在,笔者准备在主窗口与iframe之间传递一个信息
3.2 访问属性
将对方访问的属性和方法挂载到window对象上,从而主窗口通过document.querySelector('iframe').contentWindow.<xxx>访问iframe,iframe通过window.parent.<xxx>访问主窗口的属性或方法
示例代码如下:
// parent.html
const parentMessage = {
name: '李四',
age: 20
};
Reflect.defineProperty(window, 'parentMessage', {
value: parentMessage
});
// 访问子窗口的全局变量
const iframe = document.querySelector('iframe');
setTimeout(() => {
document.querySelector('code').innerHTML = JSON.stringify(iframe.contentWindow.childMessage)
}, 1000); // 等待iframe初始化完毕
// children.html
const childMessage = {
name: '张三',
age: 18
};
Reflect.defineProperty(window, 'childMessage', {
value: childMessage
});
document.querySelector('code').innerHTML = JSON.stringify(parent.parentMessage)
结果如下:

注意,这种方法只适用与同源URL,非同源URL访问对方的属性会出现类似错误:
Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:5500" from accessing a cross-origin frame
3.3 消息发送与监听
主窗口向iframe发送信息postMessage(),并监听子对象的信息,iframe监听主窗口信息并向主窗口发送信息
示例代码如下:
// parent.html
const parentMessage = {
name: '李四',
age: 20
};
const iframe = document.querySelector('iframe');
iframe.onload = function () {
// 发送消息
iframe.contentWindow.postMessage(parentMessage, '*');
};
// 接收消息
window.addEventListener('message', function (event) {
const code = document.querySelector('code');
code.innerHTML = JSON.stringify(event.data);
});
// children.html
const childrenMessage = {
name: '张三',
age: 18
};
// 接收消息
window.addEventListener('message', function (event) {
// 发送消息
window.parent.postMessage(childrenMessage, '*');
const code = document.querySelector('code');
code.innerHTML = JSON.stringify(event.data);
});
结果和上面是一样的:

这种方法同源URL与非同源URL都适用
4. Postmate
Postmate 是一个 JavaScript 库,用于简化主窗口和 iframe 之间的跨域通,它基于 window.postMessage() 方法,并提供了一种简单的方式来在主窗口和 iframe 之间发送和接收消息
Postmate 的GitHub地址为:dollarshaveclub/postmate: A powerful, simple, promise-based postMessage library. (github.com)
Postmate的特性有:
- 基于 promise 的 API,用于优雅和简单的通信
- 安全的双向父 <-> 子握手,并带有消息验证
- 子代暴露一个可检索的模型对象,父代可以访问
- 子代发出事件,父代可以监听
- 父代可以调用子代的函数
- 零依赖性,如果需要的话,可以为 Promise API 提供自己的 polyfill 或抽象
- 轻量级,大小约为1.6kb(缩小和压缩后)
Postmate 的主要用法就是:
- 主窗口建立握手程序,握手成功后可向iframe获取数据,监听事件以及远程调用函数
- iframe建立握手模型,可包含数据、函数等,成功握手后可向主窗口触发事件
大致用法就是如此,有个问题:iframe向主窗口发送数据倒是看起来很简单,直接在主窗口里获取即可,但是如何在iframe里获取主窗口的数据呢,如何才能将主窗口的数据发送给iframe呢?
方法之一是:使用iframe建立的握手模型里远程调用函数,将主窗口传给iframe的数据作为函数的参数(这一点感觉很别扭)
示例代码如下:
parent.html:
<script src="https://cdn.jsdelivr.net/npm/postmate@1.5.2/build/postmate.min.js"></script>
<div id="parent">
<!-- <iframe src="./children.html" frameborder="0"></iframe> -->
<div id="panel">
<h3>主窗口</h3>
<code></code>
</div>
</div>
<script>
const parentMessage = {
name: '李四',
age: 20
};
// 建立握手程序
const handshake = new Postmate({
container: document.querySelector('#parent'), // 注意将原iframe注释掉
url: './children.html'
});
// 握手成功后
handshake.then(child => {
// 监听事件
child.on('some-event', data => console.log(data)); // Logs "Hello, World!"
// 远程调用函数,将主窗口传给iframe的数据作为函数的参数
child.call('changeParentMessage', parentMessage);
// 获取数据
child.get('childMessage').then(data => {
document.querySelector('code').innerHTML = JSON.stringify(data)
});
});
</script>
children.html:
<script src="https://cdn.jsdelivr.net/npm/postmate@1.5.2/build/postmate.min.js"></script>
<div id="container">
<h3>子窗口</h3>
<code></code>
</div>
<script>
const childMessage = {
name: '张三',
age: 18
};
const handshake = new Postmate.Model({
// 建立握手模型 Property values may be functions, promises, or regular values
childMessage: childMessage,
parentMessage: {},
changeParentMessage: function (newMessage) {
this.parentMessage = newMessage;
document.querySelector('code').innerHTML = JSON.stringify(this.parentMessage);
}
});
//成功握手后可向主窗口触发事件
handshake.then(parent => {
parent.emit('some-event', 'Hello, World!');
});
</script>
结果如下:

5. 参考资料
[1] <iframe> - HTML(超文本标记语言) | MDN (mozilla.org)
[2] window.parent - Web API 接口参考 | MDN (mozilla.org)
[3] HTMLIFrameElement.contentWindow - Web API 接口参考 | MDN (mozilla.org)
[4] iframe跨域通信(postMessage) - 掘金 (juejin.cn)
[5] window.postMessage - Web API 接口参考 | MDN (mozilla.org)
[6] dollarshaveclub/postmate: A powerful, simple, promise-based postMessage library. (github.com)
[7] 零基础学习 Postmate库 - 掘金 (juejin.cn)
iframe与主窗口通信的更多相关文章
- PyQt5 笔记(04):主窗口卡死问题
本文基于:windows 7 + python 3.4 知识点: 1. 将 time.sleep 替换为 QTimer 2. 将 time.sleep 放入到 QThread 3. 使用 QThrea ...
- 使用iframe实现上下窗口结构及登录页全窗口展示Demo
iframe.html 首页 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> < ...
- 解决iframe作为子窗口,刷新后iframe页面跳转到其它页面的问题
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/5990262.html 前言: 在开发网站时,尤其是管理后台,我们经常会使用iframe作为内容窗 ...
- 【2016-11-6】【坚持学习】【Day21】【子窗口关闭时,同步关闭它的主窗口(方法二)】
根据上文,在子窗口设置一个委托.然后在子窗口关闭事件,执行委托实例,然后在主窗口增加监听委托的方法.... 想想,本事关闭事件就是一个特殊的委托.那么干嘛还要特意去声明一个新的呢?多此一举. 于是有下 ...
- Qt5 主窗口组成
1. 菜单栏 菜单是一系列命令的列表.为了实现菜单.工具栏按钮.键盘快捷键等命令的一致性,Qt使用动作(Action)来表示这些命令.Qt的菜单就是由一系列的QAction动作对象构成的列表,而菜单栏 ...
- eclipse中的Console控制台视图脱离主窗口解决办法
问题:Console控制台视图由于操作不当,跑出来了,脱离了主窗口 解决:在eclipse主窗口最上面的工具条选项中,找到Window,点击里面的Reset Perspective,即可,这样视图就重 ...
- MFC主窗口架构模型
根据主窗口类型,MFC软件工程可以分为一下几种架构模型: 1.SDI(Simple Document Interface)单文档界面,一个主窗口下只编辑一份文档 2.MDI(Multiple Docu ...
- 控制窗口不能拖拉出主窗口 Dialog And Window
/* 控制窗口不能拖拉出主窗口(覆写) */var easyuiPanelOnMove = function(left, top) { if ($(this).panel('options').reS ...
- Iframe刷新父窗口的几种方式
/*Iframe刷新父窗口的几种方式在iframe的子页面中,使用onload刷新父页面的时候,遇到了一些问题. 1.目前来说,测试成功,并且兼容IE6/7和FF的刷新方式. */ <scrip ...
- ABBYY是怎么自定义主窗口的
启动 ABBYY FineReader OCR文字设别软件时,将打开其主窗口.同时将显示任务窗口,可以启动内置或自定义自动化任务. 主窗口显示目前打开的 ABBYY FineReader 文档. ● ...
随机推荐
- vue路由的两种方式(路由传参)
query和params区别 query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在 params类似 po ...
- C# 获取指定窗口的上层窗口
如何获取当前窗口层级上方的所有窗口信息 User32有函数GetWindow function (winuser.h) - Win32 apps | Microsoft Docs,可以根据已知窗口句柄 ...
- WPF 屏幕点击的设备类型
1.鼠标 可以通过Mouse相关的事件参数MouseButtonEventArgs中的数据,e.StylusDecice==null表示没有触摸设备,所以设备为鼠标 2.触笔 or 触摸 根据Styl ...
- 一文搞懂 x64 IA-64 AMD64 Inte64 IA-32e 架构之间的关系
想要搞清楚 x64.IA64.AMD64 指令集之间的关系,就要先了解 Intel 和 AMD 这两家公司在生产处理器上的发展历史. x86 处理器 1978年 Intel 生产了它的第一款 16bi ...
- 分享Zeal的全套离线文档
鉴于Zeal自身的下载速度... 为了方便大家,现在把我自己下载好的Zeal离线文档全部分享出来 百度网盘链接:https://pan.baidu.com/s/19WeEWij3evnuMWhzbHu ...
- ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗?
ChatGPT会对我们日常生活带来什么影响?这些技术会改变我们学习阅读工作方式吗? AI 这个话题很火,我也一直在关注着,很多人甚至觉得 AI 会改变世界,也许你会好奇:ChatGPT 会在三年内终结 ...
- 2022-05-12:小歪每次会给你两个字符串: 笔记s1和关键词s2,请你写一个函数, 判断s2的排列之一是否是s1的子串。 如果是,返回true; 否则,返回false。 来自字节飞书团队。
2022-05-12:小歪每次会给你两个字符串: 笔记s1和关键词s2,请你写一个函数, 判断s2的排列之一是否是s1的子串. 如果是,返回true: 否则,返回false. 来自字节飞书团队. 答案 ...
- 2022-01-13:K 个不同整数的子数组。 给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定不同的子数组为好子数组。 (例如,[1,2,3,1
2022-01-13:K 个不同整数的子数组. 给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续.不一定不同的子数组为好子数组. (例如,[1,2,3,1 ...
- 数据挖掘实践(金融风控):金融风控之贷款违约预测挑战赛(上篇)[xgboots/lightgbm/Catboost等模型]--模型融合:stacking、blending
数据挖掘实践(金融风控):金融风控之贷款违约预测挑战赛(上篇)[xgboots/lightgbm/Catboost等模型]--模型融合:stacking.blending 1.赛题简介 赛题以金融风控 ...
- 【熊子q的代码乐园】用python写个健康报备记录小系统
目录 一.前言 二.技术实现 1.概述 2. 环境 3. 技术核心 4. 数据库 5. 源代码 三.最后 一.前言 coding的一路上,遇到过许多问题,也写过一些代码去解决,回头看看还是有点意思的, ...