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 文档. ● ...
随机推荐
- [OpenCV-Python] 17 形态学转换
文章目录 OpenCV-Python:IV OpenCV中的图像处理 17 形态学转换 17.1 腐蚀 17.2 膨胀 17.3 开运算 17.4 闭运算 17.5 形态学梯度 17.6 礼帽 17. ...
- 企名片Js逆向思路
企名片Js逆向思路 这个案例不算难,简单说一下思路. 目标链接:https://www.qimingpian.cn/finosda/project/pinvestment 网站更新了https://w ...
- SQL Server 2005递归查询
WHIT XXX(列1,列2....) AS ( SELECT 列1,列2... FROM 表WHERE ID=xxxxxx UNION ALL SELECT 列1,列2.... FROM 表 WHE ...
- SVN添加新的用户
1.首先确定svn的文件夹的位置 find / -name svn 2.通过find找到svn地址之后,cd进入 /var/svn/ 下 cd /var/svn/ 3.修改/var/svn/下面的au ...
- 2022-08-31:以下go语言代码输出什么?A:江苏;B:v[“province“]取值错误;C:m.Store存储错误;D:不知道。 package main import ( “fm
2022-08-31:以下go语言代码输出什么?A:江苏:B:v["province"]取值错误:C:m.Store存储错误:D:不知道. package main import ...
- 2020-10-22:谈谈java中的LongAdder和LongAccumulator的相同点和不同点。
福哥答案2020-10-22: 简单回答:相同点:都是多个单元操作.不同点:LongAdder相加,LongAccumulator自定义计算规则. 中级回答:相同点: LongAddr与LongAcc ...
- 2021-05-21:给定一个数组arr,先递减然后递增,返回arr中有多少个绝对值不同的数字?
2021-05-21:给定一个数组arr,先递减然后递增,返回arr中有多少个绝对值不同的数字? 福大大 答案2021-05-21: 双指针.左指针最左,符合条件时右移:右指针最右,符合条件时左移.左 ...
- Redis 高级特性 Redis Stream使用
Redis Stream 简介 Redis Stream 是 Redis 5.0 版本新增加的数据结构. Stream从字面上看是流类型,但其实从功能上看,应该是Redis对消息队列(MQ,Messa ...
- Windows与网络基础
Windows 基础命令 一.目录和文件的应用操作 1.cd命令 cd /d d:\ //切换到d盘目录,因为改变了驱动器,所以要加上/d选项 cd c:\ //如果没有改变驱动器号,就不需要加/d选 ...
- ARC143
ARC143 考试情况:一眼订正,鉴定为做出前三题. A - Three Integers 以前做过 \(n\) 个数的版本,当时还被某人嘲讽说"堆,贪心,这都做不出来?". \( ...