window对象传递消息
随着前端业务逻辑的愈发复杂化,常需要在不同窗口之间进行数据通信,本文介绍了如何使用message事件进行数据通信的行为。
1. window.top对象
window.top
是在Web浏览器环境中使用的JavaScript全局对象,它提供了对顶级窗口的引用。顶级窗口是当前浏览器标签页或窗口层次结构中的最高级别窗口。
以下是一些关于 window.top
的重要信息:
window.top
提供了对顶级窗口的引用,通常是指当前浏览器标签页的最顶层窗口对象。这个顶级窗口对象可以访问和操作整个窗口层次结构,包括其他嵌套框架或窗口。- 通过
window.top
,你可以访问顶级窗口的属性和方法,例如window.top.document
表示顶级窗口的文档对象,window.top.location
表示顶级窗口的URL地址等。 - 使用
window.top
可以方便地在嵌套的框架或窗口之间进行通信和操作。通过window.top
对象,可以向上遍历窗口层次结构,与其他同级或父级窗口进行交互。 - 需要注意的是,由于安全性限制,跨域的窗口访问可能会受到限制。如果脚本位于不同的域名或协议下,访问
window.top
的属性和方法可能会受到同源策略的限制。
总之,window.top
是一个指向顶级窗口对象的引用,它提供了在Web浏览器环境中访问和操作顶级窗口的功能,用于处理窗口间的通信和操作。
2. window对象和消息机制
- iframe标签对象上有一个名为
contentWindow
的对象,这个对象存在的价值为:当A.html中使用ifame引用b.html的时候,b文档对应的contentWindow对象实际上提供了a和b进行通信的接口; - contentWindow对象是b文档在a文档中被引用的时候的接口对象,记为contentWindow::BinA,对应的是b文档本身的window对象,记为window::BinB,数据流从a到b的过程,实际上是从contentWindow::BinA到window::BinB的过程;
- 也就是
contentWindow::BinA
发消息,而window::BinB
接受消息:
// a.html
contentWindow.postMessage('msg from a', '*')
// b.html
window.addEventListener('message', function(e){
console.log(e.data);
})
3. contentWindow对象的获取
强调一下:contentWindow对象是iframe标签对象的一个属性:
const contentWindow = document.getElementById("bFrame").contentWindow;
4. iframe的name属性
在iframe标签上设置name属性,可以方便在通信过程中识别数据源,如下:
<iframe src="b.html" id="bFrame" name="bFrame" style="width:100%;height:600px;"></iframe>
5. message事件的事件对象
window使用addEventListener监听message事件,对应的事件对象e中有两个常用的属性:
- data: 其值为传递的数据
- source: 表示数据源,数据源的类型是Window
6. 实例
6.1 项目结构
mkdir test-window && cd test-window
touch index.html parent.html child.html
6.2 index.html中的内容
<!DOCTYPE html>
<html>
<head>
<title>Communication Example</title>
<script>
function sendMessageToParent() {
// 1. 找到文档对应的接口对象contentWindow
var parentFrame = document.getElementById("parentFrame").contentWindow;
// 2. 使用contentWindow上的postMessage方法传递信息
parentFrame.postMessage("Hello from top Window!", "*");
}
</script>
</head>
<body>
<h1>Communication Example</h1>
<!-- 点击按钮之后获取parent.html对应的window对象,使用这个对象上的postMessage向parent.html传递消息
也就是说,contentWindow是内嵌html提供的通信接口 -->
<button onclick="sendMessageToParent()">Send Message to Parent</button>
<iframe src="parent.html" id="parentFrame" name="parentFrame" style="width:100%;height:600px;"></iframe>
</body>
</html>
6.3 parent.html中的内容
<!DOCTYPE html>
<html>
<head>
<title>Parent Window</title>
</head>
<body>
<h1>Parent Window</h1>
<iframe src="child.html" id="childFrame" name="childFrame" style="width:100%;height:400px;"></iframe>
<script>
// 1. 本文档的window对应的就是在其它文档中标签对象上的contentWindow属性
// 2. 使用window监听message事件,即可订阅从其他文档中通过postMessage方法传递过来的信息
window.addEventListener("message", function(event) {
if (event.source === window.top) {
console.log("Message received from Top Window:", event.data);
// 3. 将收到的消息传递给下一层文档
transformMessageToChild(event.data);
}
});
// 4. 同样是先获取contentWindow,然后使用其传递消息
function transformMessageToChild(data) {
var childFrame = document.getElementById("childFrame").contentWindow;
childFrame.postMessage(`(msg from top, delivered by parent) ${data}`, "*");
}
</script>
</body>
</html>
6.4 child.html
<!DOCTYPE html>
<html>
<head>
<title>Child Frame</title>
</head>
<body>
<h1>Child Frame</h1>
<script>
window.addEventListener("message", function(event) {
if (event.source.name === "parentFrame") {
console.log("Message received from Parent Window:", event.data);
}
});
</script>
</body>
</html>
效果
点击按钮之后,控制台打印的内容:
Message received from Top Window: Hello from top Window! parent.html:26
Message received from Parent Window: (msg from top, delivered by parent) Hello from top Window! child.html:12
7. 补充:postMessage的第二个参数
-
在
postMessage()
方法中,第二个参数(targetOrigin)指定了目标窗口的来源。 -
将
'*'
作为targetOrigin
参数传递给postMessage()
方法时,它表示可以接受来自任何源(包括不同的域、协议和端口)发送的消息。具体来说,'*'
是一个通配符,用于指示无论来源是什么,都可以接收消息。这意味着消息会被发送到目标窗口的所有窗口对象(即window
对象)。 -
在真实的生产环境中,使用
'*'
应谨慎。因为它允许接受来自任何来源的消息,可能存在安全风险。为了确保安全性,最好将targetOrigin
参数设置为确切的值,以限制只接受来自特定源的消息。 -
例如,如果你的目标窗口是位于
https://example.com
域名下的页面,你可以将targetOrigin
设置为该域名:
childFrame.postMessage("Hello from Parent Window!", "https://example.com");
这样,只有来自 https://example.com
域名的窗口发送的消息才会被接受。其他来源的消息将被忽略,从而提供了更严格的安全性。