title: js中的页面间通信
date: 2016-8-10
tag: javascript
js中的页面间通信
本来准备找实习了,由于暑假回家学车,学完后回不去学校,(⊙﹏⊙)。
一些找前端实习的小伙伴都被问到了页面间通讯的问题,所以做了一点了解。
页面间通信涉及到跨域与不跨域,不跨域的通信比较简单,而跨域通信的方法也有,不过每一种也都有限制。
不跨域的通信
1.1 iframe嵌套
有些页面会通过iframe标签嵌套页面,嵌套页面即作为子页面。父与子页面之间的通信很简单。
<!-- 父页面 postMessage.html -->
<html>
<head></head>
<body>
<input id="msg" type="text" placeholder="再次输入信息"/>
<input type="button" value="发送消息" onclick="sendMsg()"/>
<!-- 子页面 getMessage.html -->
<iframe src="getMessage.html"></iframe>
</body>
</html>
<!-- 子页面 getMessage.html-->
<html>
<head></head>
<body>
<h3>这里是getMessage.html</h3>
<div id="show-msg"></div>
</body>
</html>
代码:
<script>
function sentMsg(){
var msg = document.querySelector('#msg');
var data = msg.value;
window.frames[0].document.querySelector('#show-msg').innerHTML = data;
}
</script>
在父页面中的input中输入内容,点击发送,在子页面中就会显示对应内容。
当然,如果父页面中存在多个iframe,只需通过window.frame找到对应的ifrmae,如上传递内容就可以了。
1.2 跨标签页通信 window.open
有时候,当在一个页面中点击链接,打开了一个新的页面,两个页面在相同的域下,可以实现标签页之间的通讯.
<!-- 父页面 postMessage.html -->
<html>
<head></head>
<body>
<input type="button" value="发送消息" onclick="sendMsg()"/>
<a href="getMessage.html" target="_blank" onclick="toPage(event)">点击</a>
</body>
</html>
代码:
<!-- 父页面 postMessage.html -->
<script>
var newPage;
function toPage(evt){
evt.preventDefault(); //阻止默认事件
var url = evt.target.getAttribute('href');
newPage = window.open(url,'newPage');
}
function sentMsg(){
newPage.document.querySelector('#msg').innerHTML = '这是postMessage发来的信息';
}
</script>
<!-- 子页面 getMessage.html -->
<script>
window.addEventListener('load',function(evt){
opener.document.querySelector('#show-msg').innerHTML = 'load好子页面了';
},false);
</script>
当在postMessage.html页面里点击链接,通过window.open方法打开一个新的页面,如父子页面满足”同源策略”则该方法会返回创建窗口的window对象的引用。getMessage.html添加侦听load事件,通过window.opener获取创建该窗口的window对象的引用,通过该引用可以控制该页面里元素。
关于window.open()和window.opener()参见MDN的window.open和window.opener。
跨域通信
有时候我们希望能够在不同的域名之间传递数据,但由于浏览器的“同源策略”,不同域之间的通信只能通过一些技巧来实现。以下是对是否是同源情况的判断:
http://www.a.com/b.js
http://www.a.com/b/b.js
http://www.a.com/b.js
https://www.a.com/b.js
http://127.0.0.1/b.js
http://will.a.com/b.js
http://a.com/b.js
http://www.b.com/b.js
一般跨域传递数据有3种方法,jsonp、document.domain、window.name来实现。不过,这三种方法都有各自的限制。
####2.1 jsonp实现
jsonp全称是”json padding”,json想必已经很清楚了,如果忘了就点击自己重新复习吧。JSONP是一种使用JSON数据的方式,返回的不是JSON对象,是包含JSON对象的javaScript脚本。
由于常规使用XMLHttpRequest请求只允许同域下的资源,但是script标签加载js文件却可以跨域加载,比如img标签也可以。通过使用script标签来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递 javascript对象。即在跨域的服务端生成JSON数据,然后包装成script脚本回传,就不用突破同源策略的限制,解决了跨域访问的问题。
<script>
function callback(data){
//处理数据
}
var jsonP = document.createElement('script');
jsonP.src = 'http://demo.com/server.php?req=callback';//域名随意起的
document.body.appendChild(jsonP);
</script>
服务器端的响应代码:
<?php
$req = $_GET['req'];
$data = array(
'data'=>'这是请求的数据'
);
echo $req."(json_encode($data))";
?>
这其实就是JSONP的简单实现。客户端创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。即将服务器的json数据填充(padding)作为回调函数的参数。
jsonp方式跨域也有缺陷,首先只能是通过GET方式请求数据,若操作成功,就顺利执行。出现失败则不会有提示,例如不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。当然超时后就不用管他了。同时也需要服务器相应的配合,处理请求才行。
####2.2 document.domain
每一个页面都有document.domain属性,调用这个属性,返回当前文档的域名。也可以对该属性进行赋值,不过值必须是包含基础域名的值(必须属于同一个基础域名!而且所用的协议,端口都要一致)。
例如:
//http://willing.a.com/a.html与http://will.a.com/b.html
//a.html
<html>
<head>
<script>
docuemnt.domain = 'a.com';
</script>
</head>
<body>
<frame src="http://will.a.com/b.html"></iframe>
</body>
</html>
//b.html
<html>
<head>
<script>
docuemnt.domain = 'a.com';
</script>
</head>
<body></body>
</html>
两个页面处于同一个基础域名下不同子域名下,他们之间js跨域(子域)相互调用,需要在head标签中显式设置同一基础域名,否则会失败。
通过这种方式,可以实现跨子域的数据传递,而无法实现跨基础域名的数据传递。
另外,Html5的localStorage也可以通过设置document.domain实现跨子域的数据传递。
####2.3 window.name
每个标签在打开的时候都会存在window.name属性,若没有设置一般为空。window.open函数调用时传递的第二参数即为指定创建窗口的name值。
浏览器中当一个标签页打开到关闭之间,window.name的值不变,即使在期间访问了不同域名的网站,name值都是空或手动设置的值。在浏览器控制台输入代码:
window.name = 'YouGuess';
location.href = 'http://www.qq.com';
在一个页面中通过iframe嵌入的跨域页面,也可以实现跨域传值,不过该值的大小有所限制(大约2M)。
<!-- 父页面 postMessage.html -->
<html>
<head></head>
<body>
<div id="show-msg"></div>
<input type="button" value="获取子页面name" onclick="getChildName()"/>
<!-- 子页面 getMessage.html -->
<iframe src="www.baidu.com"></iframe>
</body>
</html>
<!-- 子页面 getMessage.html-->
<html>
<head></head>
<body>
</body>
</html>
代码:
<script>
<!-- 父页面 postMessage.html -->
function getChildName(){
var show = document.querySelector('#show-msg');
var childName = window.frames[0].contentWidth.name;
show.innerHTML = childName;
}
<!-- 子页面 getMessage.html-->
window.addEventListener('load',function(){
window.name = 'YouGuess';
})
</script>
当在iframe中的页面设置好name的值,点击父页面的按键,即可显示子页面的name属性。
###3. HTML5的postMessage
HTML5提供了postMessage方法,该方法既可以跨域通信也可以用来不跨域的通信,不过在IE中目前只支持IE8+,其他浏览器基本全面支持。
otherWindow.postMessage(data,origin)
-targetSrc指目标窗口或iframe
-data指要传递的数据,html5规范可以使js的任意基本类型和可复制对象(ie8、9除外)
-origin指明目标窗口的源(协议+主机+端口号[+URL],URL会被忽略,所以可以不写)定和当前窗口同源的话设置为”/“,若”*”则代表任何页面。