本文转自:https://bbs.implug.cn/?thread-3.htm
平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题:
- 多窗口之间消息传递(newWin = window.open(…));
- 页面与嵌套的iframe消息传递。
- 同域名下可以通过parent调用父级页面的js跨域时如何调用。
这里介绍的方法是用运用postMessage方式解决
发消息方调用postMessage方法发送消息 postMessage是html5引入的API可以更方便、有效、安全的解决这些问题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
收消息方用MessageEvent监听消息。
postMessage(data,origin)方法接受两个参数
- data:要传递的数据, html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
- origin:字符串参数,指明目标窗口的源, 协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,someWindow.postMessage()方法只会在someWindow所在的源(url的protocol, host, port)和指定源一致时才会成功触发message
- event,当然如果愿意也可以将参数设置为"*",someWindow可以在任意源,如果要指定和当前窗口同源的话设置为"/"。
MessageEvent的属性
- data:顾名思义,是传递来的message
- source:发送消息的窗口对象
- origin:发送消息窗口的源(协议+主机+端口号)。
同域父子页面间通讯
代码示例:
父页面a.html:
// localhost:9011/a.html
<h1 class="header">page A</h1>
<div class="mb20">
<textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
<button style="font-size:20px;" onclick="send()">post message</button>
</div>
<!-- 不跨域的情况 -->
<iframe src="b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
<script>
function send() {//发送消息
var data = document.querySelector('#data').value;
// 注意: 只会触发当前window对象的message事件
// 也可以访问子页面的window对象,触发子页面的message事件 (window.frames[0].postMessage(...))
// window.postMessage(data, '/');
// data = {name: 'sandy', age: 20, fav: {sing: true, shop: false}}; // 也可以传普通对象
window.frames[0].postMessage(data, '/'); // 触发同域子页面的message事件(注意这里用的window.frames[0]代表发送消息给frames页面)
//window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件
}
// 当前页面执行 window.postMessage(..)或子页面执行 parent.postMessage(...) 都会触发下面的回调, messageEvent.source不同而已
window.addEventListener('message', function(messageEvent) {
var data = messageEvent.data;// messageEvent: {source, currentTarget, data}
console.info('message from child:', data);
}, false);
</script>
子页面b.html
//> localhost:9011/b.html
<h1 class="header">page B</h1>
<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>
<script>
window.addEventListener('message', function(ev) {//监听接收消息 ev包含上面写道MessageEvent的三个属性
if (ev.source !== window.parent) {return;}//这里通过判断消息来源来进行安全验证
var data = ev.data;
console.info('message from parent:', data);
}, false);
function send() {
var data = document.querySelector('#inp').value;
//window.postMessage(data, '*'); // 发送消息触发当前页面的message事件(注意这里用的window代表发送给子页面)
parent.postMessage(data, '*'); // 发送消息触发父页面的message事件(注意加上parent代表发送给父页面)
//parent.postMessage(data,'http://localhost:9011/');//若父页面的域名和指定的不一致,则postMessage失败
}
</script>
跨域父子页面间通讯
父页面a.html:
//> localhost:9011/a.html
<h1 class="header">page A</h1>
<div class="mb20">
<textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
<button style="font-size:20px;" onclick="send()">post message</button>
</div>
<!-- 跨域的情况 -->
<iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
<script>
function send() {
var data = document.querySelector('#data').value;
window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件(window.frames[0]代表发送消息给frames[0]页面),这里的第二个参数表示可以接受此消息的页面的窗口的源origin如果天*带个所有。
}
window.addEventListener('message', function(messageEvent) {
//接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
var data = messageEvent.data;
console.info('message from child:', data);
}, false);
</script>
子页面b.html
//> localhost:9022/b.html(两个页面端口号不同.为跨域的一种)
<h1 class="header">page B</h1>
<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>
<script>
window.addEventListener('message', function(ev) {
//接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
var data = ev.data;
console.info('message from parent:', data);
}, false);
function send() {
var data = document.querySelector('#inp').value;
parent.postMessage(data, 'http://localhost:9011/'); //若父页面的域名和指定的不一致,则postMessage失败(parent代表发送消息给frames[0]页面)
// parent.postMessage(data, '*'); // 触发父页面的message事件*代表任何页面都可接收,
}
</script>
通过上面的两个例子我们已经解决了,传递消息和跨域传递消息,那么怎么跨域调用JS呢。 原理很简单,就是把需要在另一个页面执行的js代码写成字符串用postMessage方法传递过去,然后另一个页面接收到后用eval()直接执行这段代码。
示例跨域情况下子页面让父页面alert(“hello!”);:
父页面a.html:
扫描二维码关注公众号,回复:
3987820 查看本文章
//> localhost:9011/a.html
<h1 class="header">page A</h1>
<div class="mb20">
<textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
</div>
<!-- 跨域的情况 -->
<iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
<script>
window.addEventListener('message', function(messageEvent) {
//接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
if(ev.origin='http://localhost:9022'){
return;
}
eval(ev.data);
}, false);
</script>
子页面b.html
//> localhost:9022/b.html(两个页面端口号不同.为跨域的一种)
<h1 class="header">page B</h1>
<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>
<script>
function send() {
var data = 'alert("hello!");';
parent.postMessage(data, '*'); // 触发父页面的message事件*代表任何页面都可接收,
}
</script>