html5 跨文档通讯
版本:HTML5
对窗口对象的message事件进行监听,使用window对象的postMessage()方法向其他窗口发送消息。
跨文档消息传送(cross-document messaging),简称XDM,指的是来自不同域的页面间传递消息。
XDM的核心是postMessage()方法。目的:向另一个地方传递数据。对于XDM而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。
postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。
接收到XDM消息时,会触发window对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接受消息(触发接受窗口的message事件)可能要经过一段时间的延迟。
有了XDM,包含<iframe>的页面可以确保自身不受恶意内容的侵扰,因为它只通过XDM为嵌入的框架通信。而XDM也可以来自相同域的页面间使用。
如果要实现从一个页面向到另一个页面传递消息,使用html5的API就可以实现。postMessage()就是用来试下跨文档消息传递的,不过这个API有局限,只能新开的页面传递消息(内嵌也可以)。
浏览器支持
![]() | ![]() | ![]() | ![]() | ![]() |
| Internet Explorer 8 以上版本支持 | ||||
postMessage()
- otherWindow是目标窗口的引用,在当前场景下就是iframe.contentWindow;
- message是发送的消息,在Gecko 6.0之前,消息必须是字符串,而之后的版本可以做到直接发送对象而无需自己进行序列化。
- targetOrigin表示设定目标窗口的origin,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。对于保密性的数据,设置目标窗口origin非常重要。
- transfer可选参数,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
当postMessage()被调用的时,一个消息事件就会被分发到目标窗口上。该接口有一个message事件,该事件有几个重要的属性:
otherwindow为要发送窗口 对象的引用,可以通过window.open()方法返回该对象,或通过对window.frames数组指定序号(index)或名字的方式来返回单个 frame所属性的窗口对象。
那么,当iframe初始化后,可以通过下面代码获取到iframe的引用并发送消息:
// 注意这里不是要获取iframe的dom引用,而是iframe window的引用
const iframe = document.getElementById('myIFrame').contentWindow;
iframe.postMessage('hello world', 'https://www.lanmper/cn');
在iframe中,通过下面代码即可接收到消息。
window.addEventListener('message', msgHandler, false);
在接收时,可以根据需要,对消息来源origin做一下过滤,避免接收到非法域名的消息导致的xss攻击。
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>跨文档消息传输示例主文档</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript">
$(function() {
// 监听message事件。
window.addEventListener("message", function(event) {
// 忽略指定URL之外的页面发送的消息。
if(event.origin != "https://www.lanmper/cn") return;
alert(event.data); // 显示消息。
}, false);
$("#iframeContent").load(function(event) {
// 向子页面发送消息
this[0].postMessage("Hello", "https://www.lanmper/cn/test/");
});
});
</script>
</head>
<body>
<header>
<h1>跨域通信示例</h1>
</header>
<iframe id="iframeContent" width="400" src="https://www.lanmper/cn/test/"></iframe>
</body>
</html>
子页面中的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript">
$(function() {
window.addEventListener("message", function(event) {
if(event.origin != "https://www.lanmper.cn") return;
$("#console").append(event.origin).append("传来的消息:").append(event.data);
// 向主页面发送消息。
event.source.postMessage("Hello, there is :" + this.location, event.origin);
}, false);
});
</script>
</head>
<body>
<p>这是iframe中的内容。</p>
<div id="console"></div>
</body>
</html>
- 通过对window对象的message事件进行监听,可以接收消息。
- 通过访问message事件的origin属性,可以获取消息的发送源。注意:发送源与网站的URL地址不是一个概念,发送源只包括域名与 端口号,为了不接收其他源恶意发送过来的消息,最好对发送源做检查。
- 通过访问message事件的data属性,可以取得消息内容(可以是任何JavaScript对象,使用JSON)。
- 使用postMessage()方法发送消息。
- 通过访问message事件的source属性,可以获取消息发送源的窗口的代理对象。
一个页面内嵌另一个页面
// index.jsp页面(源页面)
<iframe src="http://localhost:8090/a"></iframe>
<script type="text/javascript">
// 跨域发送数据到目标源
window.onload = function ()
{
document.getElementsByTagName("iframe")[0].contentWindow.postMessage({name: 'wujiang'}, "http://localhost:8090");
}
// 监听目标源发送过来的数据
window.addEventListener('message', function (e)
{
console.log('http://localhost:8090/a.ftl ===> http://localhost:8080/index.jsp');
console.log(e);
})
</script>
<script type="text/javascript">
// a.ftl页面(目标页面)
// 监听其他窗口是否发送数据
if (window.addEventListener)
{
window.addEventListener('message', function (e) {
if (e.origin !== 'http://localhost:8080') {
return
}
console.log('http://localhost:8080/index.jsp ===> http://localhost:8090/a.ftl')
// 打印数据
console.log(e)
// 回传数据
e.source.postMessage('hello wujiang', e.origin)
}, false)
} else if (window.attachEvent) {
window.attachEvent('onmessage', function (e) {
if (e.origin !== 'http://localhost:8080') {
return
}
console.log('http://localhost:8080/index.jsp ===> http://localhost:8090/a.ftl')
// 打印数据
console.log(e)
// 回传数据
e.source.postMessage('hello wujiang', e.origin)
}, false)
}
</script>
- data:发送的数据
- origin:源IP地址
- source:源窗口,可以通过source返回数据到源窗口
一个页面打开另一个页面
// 打开一个新窗口
var newWin = window.open('http://localhost:8090/a');
// 向新窗口发送数据
setTimeout(function () {
console.log('http://localhost:8080/a.jsp ===> http://localhost:8090/a.ftl');
newWin.postMessage({name: 'wujiang'}, 'http://localhost:8090');
}, 1000);





