工作中遇到了弹出新窗口间以及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.关闭父窗口
可将关闭方法写在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>
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的属性,与内容无关。