下面本人来谈谈iframe之间通信问题及iframe自适应高度问题。
1. iframe通信 分为:同域通信 和 跨域通信。所谓同域通信是指 http://localhost/demo/iframe/iframeA.html 下的a.html页面嵌套 iframe 比如: <iframe src="http://localhost/demo/iframe/iframeB.html" id="iframeA" name="iframeA">的B.html页面,这两个页面数据进行通信,比如我想在父页面A.html 调用子页面当中的函数 我们很容易想到或者google下 document.getElementById('iframeA').contentWindow.b(); 这种方法,其中b 是子页面B.html中的一个函数。但是这样调用下有个问题我纠结了很久,就是既然在火狐下报这样的错误, 如下:
b不是个函数 但是我在子页面明明定义了这么一个函数,那么为什么会报这样的错误呢?经过仔细分析及google,发现有这么一个问题需要理解,当iframe没有加载完成后 我就去执行这个js会报这样的错误,所以就试着在火狐下 用iframe.onload 这个函数 进行测试,果然没有报错,是正确的 所以就确定是这个问题。所以就想写个兼容IE和火狐 google写个函数 来确定iframe已经加载完成!,其实给个回调函数来调用我们上面的方法。
综合上面的思路 就可以写个这样的代码:
<iframe src="http://localhost/demo/iframe/iframeB.html" id="iframeA" name="iframeA"></iframe>
<div id="topName">topNddddddddddddddddame</div>
<script>
function A(){
alert("A");
}
var iframe = document.getElementById('iframeA');
iframeIsLoad(iframe,function(){
var obj = document.getElementById('iframeA').contentWindow;
obj.b();
});
function iframeIsLoad(iframe,callback){
if(iframe.attachEvent) {
iframe.attachEvent('onload',function(){
callback && callback();
});
}else {
iframe.onload = function(){
callback && callback();
}
}
}
</script>
B.html 代码如下:
var b = function(){
alert("B");
}
2.子页面调用父页面的函数很简单,只要这样搞下就ok了,window.parent.A();
3. 子页面取父页面元素的值: window.parent.document.getElementById("topName").innerHTML等方法。
二: iframe跨域通信。
iframe跨域访问一般分为2种情况,第一种是同主域,不同子域的跨域。 第二种是:不同主域跨域。
一、 是同主域下面,不同子域之间的跨域;可以通过document.domain 来设置相同的主域来解决。
假如现在我有个域 abc.example.com 下有个页面叫abc.html, 页面上嵌套了一个iframe 如下:<iframe src="http://def.example.com/demo/def.html" id="iframe2" style="display:none;"></iframe>,我想在abc域下的页面abc.html 访问 def域下的def.html 我们都知道由于安全性 游览器的同源策略的限制,js不能操作页面不同域下 不同协议下 不同端口的页面,所以就要解决跨域访问了,假如父页面abc.html 页面有个js函数:function test(){console.log(1);}; 我想在子页面调用这个函数 还是按照上面的同域方式调用 parent.test();这样,通过在火狐下看 已经跨域了 解决的办法是 在各个js函数顶部 加一句 document.domain = 'example.com',就可以解决了。abc.html代码如下:
<iframe src="http://def.example.com/demo/def.html" id="iframe2" style="display:none;"></iframe>
// 跨域 子页调用父页的 函数 (假设是下面test函数)
document.domain = 'example.com';
function test(){console.log(1);};
def.html代码如下:
/*
* 子页调用父页的方法
*/
document.domain = 'example.com';
//window.top.test();
window.parent.test();
还是这两个页面 我想父页调用子页 如下方法:
a.html代码如下:
/*
* 跨域 父页想调用子页的的函数
*/
document.domain = 'example.com';
var iframe = document.getElementById('iframe2');
iframeIsLoad(iframe,function(){
var obj = iframe.contentWindow;
obj.child();
});
function iframeIsLoad(iframe,callback){
if(iframe.attachEvent) {
iframe.attachEvent('onload',function(){
callback && callback();
});
}else {
iframe.onload = function(){
callback && callback();
}
}
}
假如现在def.html页面有个child函数 代码如下:
document.domain = 'example.com';
function child(){console.log('我是子页');}
就可以跨域调用了 不管是子页面调用父页面 还是父页面调用子页面。一切ok!
二、 是不同主域跨域;
虽然google有几种方法关于不同主域上的跨域问题 有通过location.hash方法或者window.name方法或者html5及flash等等,但是我觉得下面iframe这种方法值得学习下,
如下图所示:域a.com的页面request.html(即http://a.com/demo/ajax/ajaxproxy/request.html)里面嵌套了一个iframe指向域b.com(http://b.com/demo/ajax/ajaxproxy/response.html)的response.html,而response.html里又嵌套了域a.com的proxy.html。

思路:要实现a.com域下的request.html页面请求域b.com下的process.php,可以将请求参数通过url传给response.html,由response.html向process.php发起真正的ajax请求(response.html与process.php都属于域b.com),然后将返回的结果通过url传给proxy.html,最后由于proxy.html和request.html是在同个域下,所以可以在proxy.html利用window.top 将结果返回在request.html完成真正的跨域。

ok, 先看看页面结构
a.com域下有:
request.html
proxy.html
b.com域下有:
response.html
process.php
先来看看request.html页面如下:
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
</head>
<body>
<p id= "result" >这里将会填上响应的结果</p>
<a id= "sendBtn" href= "javascript:void(0)" >点击,发送跨域请求</a>
<iframe id= "serverIf" style= "display:none" ></iframe> <script>
document.getElementById( 'sendBtn' ).onclick = function () {
var url = 'http://b.com/demo/ajax/ajaxproxy/reponse.html' ,
fn = 'GetPerson' ,
reqdata = '{"id" : 24}' ,
callback = "CallBack" ; CrossRequest(url, fn, reqdata, callback);
} function CrossRequest(url,fn,reqdata,callback) {
var server = document.getElementById( 'serverIf' );
server.src = url + '?fn=' +encodeURIComponent(fn) + "&data=" +encodeURIComponent(reqdata) + "&callback=" +encodeURIComponent(callback);
}
function CallBack(data) {
var str = "My name is " + data.name + ". I am a " + data.sex + ". I am " + data.age + " years old." ;
document.getElementById( "result" ).innerHTML = str;
}
</script>
</body>
</html>
|
这个页面其实就是要告诉response.html:我要让你执行你定义好的方法GetPerson,并且要用我给你的参数'{"id" : 24}'。response.html纯粹是负责将CallBack这个方法名传递给下一位仁兄proxy.html,而proxy.html拿到了CallBack这个方法名就可以执行了,因为proxy.html和request.html是同域的。
response.html代码如下:
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
</head> <body>
<iframe id= "proxy" ></iframe>
<script>
function _request (reqdata,url,callback) {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject( "Microsoft.XMLHTTP" );
} xmlhttp.onreadystatechange = function (){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var data = xmlhttp.responseText;
callback(data);
}
}
xmlhttp.open( 'POST' ,url);
xmlhttp.setRequestHeader( "Content-Type" , "application/json; charset=utf-8" );
xmlhttp.send(reqdata);
}
function _getQuery(key) {
var query = location.href.split( '?' )[1],
value = decodeURIComponent(query.split(key + "=" )[1].split( "&" )[0]);
return value;
}
function GetPerson(reqdata,callback) {
var url = 'http://b.com/demo/ajax/ajaxproxy/process.php' ;
var fn = function (data) {
var proxy = document.getElementById( 'proxy' );
proxy.src = "http://a.com/demo/ajax/ajaxproxy/Proxy.html?data=" + encodeURIComponent(data) + "&callback=" + encodeURIComponent(callback);
};
_request(reqdata, url, fn);
} ( function (){
var fn = _getQuery( 'fn' ),
reqdata = _getQuery( "data" ),
callback = _getQuery( "callback" );
eval(fn + "('" + reqdata + "', '" + callback + "')" );
})();
</script>
</body>
</html>
|
这里其实就是接收来自request.html的请求得到请求参数和方法后向服务器process.php发出真正的ajax请求,然后将从服务器返回的数据以及从request.html传过来的回调函数名传递给proxy.html。
接下来看看php代码如下,其实就是想返回一个json数据:
<?php
$data = json_decode(file_get_contents( "php://input" ));
header( "Content-Type: application/json; charset=utf-8" );
echo ( '{"id" : ' . $data->id . ', "age" : 24, "sex" : "boy", "name" : "huangxueming"}' );
?>
|
最后就是proxy.html代码:
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
</head> <body>
<script>
function _getUrl(key) {
var query = location.href.split( "?" )[1],
value = decodeURIComponent(query.split(key + "=" )[1].split( "&" )[0]);
return value;
}
( function () {
var callback = _getUrl( "callback" ),
data = _getUrl( "data" );
eval( "window.top." + decodeURIComponent(callback) + "(" + decodeURIComponent(data) + ")" );
})();
</script>
</body>
</html>
|
这里也是最后一步了,proxy终于拿到了request.html透过response.html传过来的回调函数名以及从response.html直接传过来的响应数据,利用window.top执行request.html里定义的回调函数。
三:iframe高度自适应的问题。
iframe高度自适应分为2种,一种是同域下自适应 另外一种是跨域下自适应,下面我们来看看同域下iframe高度自适应的问题。
1.同域下iframe高度自适应的问题:
思路:获取被嵌套iframe元素,通过JavaScript取得被嵌套页面最终高度,然后在主页面进行设置来实现。
假如我们demo有iframe1.html和iframe2.html 下面贴上iframe1.html代码如下:
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
<style>
*{margin:0;padding:0;}
</style>
</head> <body>
<iframe src= "http://a.com/demo/ajax/iframeheight/iframe2.html" style= "width:100%;border:1px solid #333;" frameborder= "0" id= "iframe" ></iframe> <script>
window.onload = function () {
var iframeid = document.getElementById( 'iframe' );
if (iframeid && !window.opera) {
if (iframeid.contentDocument && iframeid.contentDocument.body.offsetHeight) {
iframeid.height = iframeid.contentDocument.body.offsetHeight;
} else if (iframeid.Document && iframeid.Document.body.scrollHeight){
iframeid.height = iframeid.Document.body.scrollHeight;
}
}
}
</script>
</body>
</html>
|
iframe2.html
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
<style>
*{margin:0;padding:0;}
</style>
</head> <body>
<div style= "height:500px;" ></div>
</body>
</html>
|
就可以动态设置iframe1页面的高度为iframe2的高度了。
2. 跨域下iframe高度自适应的问题。
首先我们知道iframe跨域我们是不能用上面js方式来控制了,所以我们只能用个中间键 我们可以在a.com域下iframe1.html页面嵌套一个b.com域下的iframe2.html页面,然后我在iframe2.html页面嵌套个和iframe1.html相同域的iframe3.html页面了,这样的话 iframe1.html和iframe3.html就可以无障碍的进行通信了,因为页面iframe2.html嵌套iframe3.html,所以iframe2.html可以改写iframe3.html的href值。
iframe1中的内容:
iframe1.html内容主要接受iframe3.html页面传过来的内容并且去完成相应的操作。iframe1.html代码如下:
<iframe src="http://b.com/demo/ajax/iframeheight/iframe2.html" style="width:400px;height:200px;" id="iframe"></iframe>
<script>
var ifr_el = document.getElementById("iframe");
function getIfrData(data){
ifr_el.style.height = data+"px";
}
</script>
iframe2.html中的内容:
iframe2.html内容是怎么把值传给iframe3.html页面,刚才说了是将值传递到iframe3.html页面的href中,所以只要修改iframe的src就可以,因为不用刷新C页面,所以可以用过hash的方式传递给iframe3.html页面.iframe2.html代码如下:
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
<style>
*{margin:0;padding:0;}
</style>
</head> <body>
<iframe id= "iframe" src= "http://a.com/demo/ajax/iframeheight/iframe3.html" width= "0" height= "230px" ></iframe> <script>
var oldHeight = 0,
ifr_el = document.getElementById( "iframe" );
t && clearInterval(t);
var t = setInterval( function (){
var height = document.body.scrollHeight;
if (oldHeight != height) {
oldHeight = height;
ifr_el.src += '#' +oldHeight;
}
},200);
</script>
</body>
</html>
|
可以看到 默认情况下 iframe1.html 页面我给iframe2.html的高度是200像素 但是在iframe2.html我给iframe3.html高度是230像素,那么正常情况下是有滚动条的,那么现在我是想在iframe2.html获取滚动条的高度,把高度传给通过iframe3.html的src里面去,然后在iframe3.html页面里获取这个高度值 传给iframe1.html(因为iframe1.html和iframe3.html是同域的),所以iframe1.html能取到这个高度值,再设置下本身的高度就是这个值就ok了。
iframe3.html页面的唯一功能就是接收iframe2.html页面通过href传进来的值并且传递给iframe1.html页面,可到iframe2.html页面传来的值可以通过一个定时器不停去查看location.href是 否被改变,但是这样感觉效率很低,还有个方式就是在新的浏览器中通过onhashchange事件 (IE8+,Chrome5.0+,Firefox3.6+,Safari5.0+,Opera10.6+)来监听href的改变。
iframe3.html代码如下:
<script>
var oldHeight = 0; t && clearInterval(t);
var t = setInterval( function (){
var height = location.href.split( '#' )[1];
if (height && height != oldHeight) {
oldHeight = height;
if (window.parent.parent.getIfrData) {
window.parent.parent.getIfrData(oldHeight);
}
}
},200);
</script>
|
这样就可以解决通过跨域实现iframe自适应高度的问题了。
#1楼 海之澜 2013-12-04 09:15
#2楼 土豆哥哥 2014-08-12 22:51
#3楼 世界万物 2016-07-29 18:16
#4楼 James2017 2016-10-21 18:01
有知道答案的大神吗?
#5楼 暗夜心慌方 2016-11-15 10:09
我也遇到同样问题,请问你解决了没?
#6楼 litblack 2018-01-02 14:53
#7楼[楼主] 龙恩0707 2018-01-02 23:00
自己多看看哪里不对哦
#8楼 litblack 2018-01-03 10:34
完全跨域获取高度:
1、ifr_el.src += '#' +oldHeight,每次设置高度都应该取最后一个数值, location.href.split('#')[1];获取不对,
2、document.body.scrollHeight 不同浏览器差别太大,可能包含boder的高度和marging高度,导致获取值不正确,