web页面间通信:从跨窗口到跨域

工作中遇到了弹出新窗口间以及iframe包含中的跨域问题,将使用方法以及解决方案记录下来,希望对遇到相同问题的朋友能有所帮助。
注:示例代码必须运行在web服务器下,否则调用功能会报错。

1.新窗口打开页面

通过给<a>标签增加target="_blank"属性
<a href="newWin.html" target="_blank">
or
通过js方法window.open();
如果没有其他操作控制,只是打开一个新窗口,建议采用第一种方法。
通过js方法打开新窗口,适用于增加控制判断,包括两个窗口之间的通信。
1.1父窗口(原窗口)控制子窗口(新窗口)
A.设置一个全局变量newWin,用来存储新窗口的window对象。
var newWin;
B.新建一个打开新窗口方法,并将新窗口的window对象赋给newWin
function openNewWin() {
	newWin = window.open("newWin.html"); 
}
C.通过newWin对象,对子窗口执行相关操作。如:
newWin.location.reload();//刷新子窗口
newWin.close();//关闭子窗口
newWin.newWinFn()//调用子窗口中的方法
D.如果存在打开多个窗口的情况,可声明一个全局数组变量newWins 用来存储新窗口window对象
newWins[newWins.length-1]//最后打开的一个窗口
newWins[0]//第一个打开的窗口
附完整代码:
parent.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Parent</title>
<script type="text/javascript">
	var newWins= [];


	function openNewWin() {
		newWins.push(window.open("newWin.html"));
	}


	function refreshNewWin() {
		newWins[newWins.length-1].location.reload();
	}


	function closeNewWin() {
		newWins[newWins.length-1].close();
		newWins.pop();
	}
	function callNewWinFn() {
		newWins[newWins.length-1].newWinFn();
	}
</script>
</head>
<body>
	<div>
		<h1>This is the Parent html</h1>
		<span>
			<a href="newWin.html" target="_blank">OPEN</a> the newWin.
		</span>
		<hr />
		<span>
			<a href="###"  οnclick="openNewWin()">OPEN</a> the newWin by js.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="closeNewWin()">CLOSE</a> the newest newWin.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="refreshNewWin()">REFRESH</a> the newest newWin.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="callNewWinFn()">CALL</a> the newest newWin function.
		</span>
	</div>
</body>
</html>
newWin.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>newWin</title>
<script type="text/javascript">
	function newWinFn() {
		alert('This is new window!!');
	}
</script>
</head>
<body>
	<div>
		<h1>This is the newWin html</h1>
	</div>
</body>
</html>
1.2子窗口(新窗口)控制父窗口(原窗口)
A.通过window.opener获取父窗口window对象;
var parentWin = window.opener;
B.通过parentWin对象,对窗口执行相关操作。如:
parentWin.location.reload();//刷新父窗口
parentWin.newWinFn()//调用父窗口中的方法
C.关闭父窗口
通过parentWin.close();方法关闭,可能会被浏览器阻止。
可将关闭方法写在parent.html页面中。如果使用window.close()在浏览器中可能会因为
Scripts may close only the windows that were opened by it. 
而被阻止。解决方法:
在parent.html页面中增加方法
function closeWin() {
	window.open("","_self");    
	window.close(); 
}

在子窗口中通过parentWin.closeWin()关闭父窗口。

或者在子窗口中通过

parentWin.window.open("","_self");    
parentWin.window.close(); 

可通过parentWin.closed属性判断窗口是否关闭。

附完整代码:
parent.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Parent</title>
<script type="text/javascript">
	function parentFn() {
		alert('This is parent window!!');
	}
	function closeWin() {
		window.open("","_self");    
		window.close(); 
	}
</script>
</head>
<body>
	<div>
		<h1>This is the Parent html</h1>
		<span>
			<a href="newWin.html" target="_blank">OPEN</a> the newWin.
		</span>
	</div>
</body>
</html>
newWin.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>newWin</title>
<script type="text/javascript">
	var parentWin = window.opener;
	
	function refreshParent() {
		parentWin.location.reload()
	}
	
	function closeParent() {
		if(!parentWin.closed) {
			parentWin.closeWin();
		}
		if(!parentWin.closed) {
			parentWin.window.open("","_self");    
			parentWin.window.close();  
		}
	}
	
	function callParentFn() {
		parentWin.parentFn();
	}
</script>
</head>
<body>
	<div>
		<h1>This is the newWin html</h1>
		<span>
			<a href="###"  οnclick="closeParent()">CLOSE</a> the parent win.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="refreshParent()">REFRESH</a> the parent win.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="callParentFn()">CALL</a> the parent function.
		</span>
	</div>
</body>
</html>
1.3父窗口与子窗口间的相互调用
一个例子明确两个窗口之间的相互调用
parent.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Parent</title>
<script type="text/javascript">
	var newWin;
	var i = 0;
	
	function openNewWin() {
		newWin = window.open("newWin.html");
	}
	
	function receivedNewWin() {
		alert(i++);
		if(5 === i){
			return;
		}
		newWin.receivedParent();
	}
</script>
</head>
<body>
	<div>
		<h1>This is the Parent html</h1>
		<span>
			<a href="###"  οnclick="openNewWin()">OPEN</a> the newWin by js.
		</span>
	</div>
</body>
</html>
newWin.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>newWin</title>
<script type="text/javascript">
	var parentWin = window.opener;
	
	function sendToParent() {
		parentWin.receivedNewWin();
	}
	function receivedParent(){
		alert(parentWin.i++);
		sendToParent();
	}
</script>
</head>
<body>
	<div>
		<h1>This is the newWin html</h1>
		<span>
			<a href="###"  οnclick="sendToParent()">SEND</a> to the parent win.
		</span>
	</div>
</body>
</html>
小结:父窗口与子窗口之间的调用,主要是通过两个值window.open()返回的newWin,以及window.opener返回的parentWin。
但是从用户体验角度出发,应尽量减少对窗口的控制。弹出新窗口和关闭窗口都应该算作浏览器行为,而且容易被浏览器所禁止。
可以通过全屏的dialog代替新窗口打开页面,通过AJAX进行局部刷新,尽量减少整个页面的跳转切换。

2.iframe包含页面

存在iframe包含的页面中,可以在外部页面(父页面)中,通过iframe对象的contentWindow获取包含页面的window对象。
在内部页面(子页面)中,通过window.parent获取外部页面的window对象。类似于上文中提到的newWin和opener对象,这里不再复述,给出一个demo代码。
代码如下:
location.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Location</title>
<script type="text/javascript">
	var ifr;
	window.onload = function(){
		ifr = document.getElementsByTagName('iframe')[0];
	}
	function locationFn() {
		alert("This is the location html!");
		callInsideFn();
	}
	function callInsideFn() {
		ifr.contentWindow.insideFn();
	}
</script>
<style type="text/css">
	iframe {
		width: 500px;
	}
</style>
</head>
<body>
	<div>
		<h1>This is the location html</h1>
		<span>
			<a href="###"  οnclick="callInsideFn()">CALL</a> the inside function.
		</span>
		<br />
		<span>
			<iframe src="inside.html"></iframe>
		</span>
	</div>
</body>
</html>
inside.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Inside</title>
<script type="text/javascript">
	function callLocationFn() {
		window.parent.locationFn();
	}
	function insideFn() {
		alert("This is the inside html!");
		callLocationFn();
	}
</script>
</head>
<body>
	<div>
		<h1>This is the inside html</h1>
		<span>
			<a href="###"  οnclick="callLocationFn()">CALL</a> the location function.
		</span>
	</div>
</body>
</html>

3.跨域简介

跨域是指两个页面间的源(origin)不同,包括协议(http/https)、端口号(port)、域名(包括二级域名以及域名对应ip地址)。出于安全考虑,不同源之间不能相互调用DOM元素以及js文件。但在实际开发过程中,有时不得不在不同源之间进行操作,下面就来详细说一下针对‘跨域’问题的解决方案。

跨域模拟:通过http://localhost:port和http://ipaddress:port(本例为http://172.16.0.154:9999)或http://127.0.0.1:port模拟不同域下的页面。

将上例location.html中的
<iframe src="inside.html"></iframe>
修改为
<iframe src="http://172.16.0.154:9999/inside.html"></iframe>
再次点击CALL时,报以下跨域错误:
Uncaught SecurityError: Blocked a frame with origin "http://localhost:9999" from accessing a frame with origin "http://172.16.0.154:9999". Protocols, domains, and ports must match.

3.1通过hash实现跨域通信

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。

url组成为 协议+域名+端口号+路径+hash+参数

location.protocol + '//' + location.hostname + ":" +location.port + location.pathname + location.hash

通过hash实现跨域通信的原理

如图所示,在域中通过iframe包含,可以在src的url后面加上hash,达到传递参数的目的。
子页面inside.html不能跨域修改location.html的hash值,我们可以在inside.html页面中,在包含一个iframe,并将hash传递给proxy.html。
此时proxy.html与location.html在同一域下,因此,可通过proxy.html修改location.html的hash值,以此达到跨域传值的效果。
代码如下:
location.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Location</title>
<script type="text/javascript">
	function showHash() {
		alert(location.hash);
	}
</script>
<style type="text/css">
	iframe {
		width: 500px;
	}
</style>
</head>
<body>
	<div>
		<h1>This is the location html</h1>
		<span>
			<a href="###"  οnclick="showHash()">SHOW</a> the inside value.
		</span>
		<br />
		<span id="ifr-span">
			<iframe src="http://172.16.0.154:9999/venus/test-5-iframe-crossdomain-hash/inside.html#location"></iframe>
		</span>
	</div>
</body>
</html>
inside.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Inside</title>
</head>
<body>
	<div>
		<h1>This is the inside html</h1>
		<span>
			<input type="text" id="inside-val">
		</span>
		<br />
		<span>
			<a href="###" οnclick="setHash();">SET</a> location hash;
		</span>
	</div>
</body>
<script type="text/javascript">
	switch(location.hash){
		case '#location':
			alert('1')
		    break;
		case '#something':
		    //do something……
		    break;
	}
	function setHash() {
// 		parent.location.hash = document.getElementById('inside-val').value;
		var ifrproxy = document.createElement('iframe');
        ifrproxy.style.display = 'none';
        ifrproxy.src = "http://127.0.0.1:9999/venus/test-5-iframe-crossdomain-hash/proxy.html#"+document.getElementById('inside-val').value;
        document.body.appendChild(ifrproxy);
	}
</script>
</html>
proxy.html
<script type="text/javascript">
parent.parent.location.hash = self.location.hash.substring(1);
</script>

缺点:数据明文传递、数据大小限制、只能传递string类型。

3.2通过window.name实现跨域通信
name 属性可设置或返回存放窗口的名称的一个字符串。
扩展:window.open()方法有四个参数,可以控制弹出窗口的显示等。
window.open(URL,name,features,replace)

name为新打开窗口的window.name属性的值。
replace为boolean类型,决定是否替换浏览器历史记录。
features可以控制新打开窗口的大小,菜单栏是否显示等。但是由于不同浏览器支持不同,所以意义不大。
w3c的详细介绍(http://www.w3school.com.cn/htmldom/met_win_open.asp)

window.name不会因为页面刷新,而丢失值,正是基于此特性,达到跨域传值的效果。

在窗口B中打开外部页面inside.html,并设置window.name。此时location.html中不能调用窗口B的window.name。此时将窗口B刷新替换为与location.html在同一域下的proxy.html页面,此时两个窗口在同一域下,因此窗口A可以调用窗口B的window.name。
代码如下:
location.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Location</title>
<script type="text/javascript">
	window.name = 'This is location!';
	var ifr;
	window.onload = function(){
		ifr = document.getElementsByTagName('iframe')[0];
	}
	function showLocation() {
		alert(window.name);
	}
	function showInside() {
		alert(ifr.contentWindow.name);
	}
    function getInside(){
    	ifr.contentWindow.location = "http://127.0.0.1:9999/venus/test-6-iframe-crossdomain-windowname/proxy.html";    // 设置的代理文件
    }
</script>
<style type="text/css">
	iframe {
		width: 500px;
	}
</style>
</head>
<body>
	<div>
		<h1>This is the location html</h1>
		<span>
			<a href="###"  οnclick="showLocation()">SHOW</a> the location's window.name.
		</span>
		<br />
		<span>
			<a href="###"  οnclick="showInside()">SHOW</a> the inside's window.name.
			<a href="###"  οnclick="getInside()">GET</a> the inside's window.name.
		</span>
		<br />
		<span id="ifr-span">
			<iframe src="http://172.16.0.154:9999/venus/test-6-iframe-crossdomain-windowname/inside.html"></iframe>
		</span>
	</div>
</body>
</html>
inside.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Inside</title>
<script type="text/javascript">
	function setWindowName() {
		window.name = document.getElementById('inside-val').value;
	}
</script>
</head>
<body>
	<div>
		<h1>This is the inside html</h1>
		<span>
			<input type="text" id="inside-val">
		</span>
		<br />
		<span>
			<a href="###" οnclick="setWindowName();">SET</a> window.name;
		</span>
	</div>
</body>
</html>
proxy.html
随意,空白页即可。window.name是窗口B的属性,与内容无关。








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值