作為一個(gè)沒(méi)有什么情懷和追求的前端工程師, 是么有怎么考慮過(guò) iframe 用法的, (畢竟從學(xué)習(xí)到工作自己扮演的一直是"前端小妹妹"的角色, 這種臟活累活一般是交給后端臭屌絲的,直到公司的全棧妹子來(lái)找我探討這個(gè)跨域通信的問(wèn)題);
真是不看不知道, 一看嚇一跳. 原來(lái) iframe 也存在跨域的問(wèn)題. 跨域和不跨域情況下和父級(jí)頁(yè)面的通信方式也是略有不同.
文中示例代碼均在github
同域情況下的父子頁(yè)面通信方式
show you the demo
父級(jí)頁(yè)面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function say() {
console.log("i am father page");
}
function callChild() {
mySon.window.say();
mySon.window.document.getElementById('button').innerHTML = '調(diào)用結(jié)束';
}
</script>
<button id="button" onclick="callChild()"> 調(diào)用兒子的方法 </button>
<iframe name="mySon" src="http://localhost:3000/demo.html?aim=xiaopang" frameborder="0"></iframe>
</body>
</html>
兒子頁(yè)面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function say() {
console.log('i am child page');
}
function callFather() {
parent.say();
parent.window.document.getElementById('button').innerHTML = '調(diào)用結(jié)束'
}
</script>
<button id="button" onclick="callFather()"> 調(diào)用爸爸的方法 </button>
</body>
</html>
以上示例功能
-
調(diào)用函數(shù):
- 父級(jí)頁(yè)面調(diào)用子頁(yè)面的函數(shù) name.window.func();
- 子頁(yè)面調(diào)用父級(jí)頁(yè)面的函數(shù) parent.window.func();
訪問(wèn)頁(yè)面元素
window都拿到了,元素不會(huì)拿骆姐。請(qǐng)自行百度争占。
ps: 注意事項(xiàng):
兒子頁(yè)面加載完成后再進(jìn)行訪問(wèn), 判斷方法:
- iframe 上添加 onload 事件;
- document.readyState == 'complete';
跨域情況下父子頁(yè)面通信的方法
如果 iframe 所鏈接的是外部的頁(yè)面, 尼瑪, 居然有跨域限制, 有跨域限制, 有跨域限制......
父頁(yè)面向子頁(yè)面?zhèn)鬟f數(shù)據(jù)
跨域情況下的父子頁(yè)面通信的原理大概就是使用 hash / 直接傳參數(shù)搞定;傾向于hash;
- 在子頁(yè)面中通過(guò)定時(shí)器監(jiān)聽(tīng) location.hash 的變化就能夠拿到父級(jí)頁(yè)面?zhèn)鱽?lái)的數(shù)據(jù)啦;
- 一次性的從父頁(yè)面 get 過(guò)來(lái)的參數(shù)中提取有用的部分,就 OK 啦;
子頁(yè)面向父頁(yè)面?zhèn)鬟f數(shù)據(jù)
跨域情況下,子頁(yè)面向父頁(yè)面?zhèn)鬟f數(shù)據(jù)的實(shí)現(xiàn)原理是使用一個(gè)代理的 iframe (就像是跨域請(qǐng)求中的代理服務(wù)器一樣), 并且保持代理的子頁(yè)面和父級(jí)頁(yè)面是同域的此時(shí)就可以通過(guò), window.top 或者 window.parent.parent 就可以獲取父級(jí)頁(yè)面的引用啦. 嗶哩嗶哩...
不 bb 上 demo
父級(jí)頁(yè)面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
我是彩筆圈圈
<script>
var i = 1;
function say(data) {
console.log('我是爸爸' + data);
}
function add() {
document.getElementById('quanquan').setAttribute('src', 'http://localhost:3000/demo2.html?aim=quanquan#'+ ++i)
}
</script>
<button onclick="add()">+1</button>
<iframe id='quanquan' src="http://localhost:3000/demo2.html?aim=quanquan#1" frameborder="0"></iframe>
</body>
</html>
子級(jí)頁(yè)面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var hash = location.hash, i = 1;
function checkData() {
if(hash !== location.hash) {
hash = location.hash
console.log('我是兒子' + hash);
}
}
function say() {
console.log('i am child page');
}
function callFather() {
document.getElementById('quanquan').setAttribute('src', 'http://localhost:4000/proxy.html#'+ ++i)
}
setInterval(checkData, 1e3);
</script>
<button id="button" onclick="callFather()"> 調(diào)用爸爸的方法 </button>
<iframe id="quanquan" style="display: none" src="http://localhost:4000/proxy.html#1" frameborder="0"></iframe>
</body>
</html>
proxy 中轉(zhuǎn)頁(yè)面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>proxy</title>
</head>
<body>
<script>
var hash = location.hash;
function callGrandFather(data) {
window.top.say(data);
};
function checkData() {
if ( hash !== location.hash ) {
callGrandFather(location.hash);
hash = location.hash;
}
}
setInterval(checkData, 1e3);
</script>
</body>
</html>