下面本人来谈谈iframe之间通讯问题及iframe自适应高度问题。javascript
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已经加载完成!,其实给个回调函数来调用咱们上面的方法。php
综合上面的思路 就能够写个这样的代码:html
<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 代码以下:html5
var b = function(){ alert("B"); }
2.子页面调用父页面的函数很简单,只要这样搞下就ok了,window.parent.A();java
3. 子页面取父页面元素的值: window.parent.document.getElementById("topName").innerHTML等方法。ajax
二: iframe跨域通讯。json
iframe跨域访问通常分为2种状况,第一种是同主域,不一样子域的跨域。 第二种是:不一样主域跨域。跨域
1、 是同主域下面,不一样子域之间的跨域;能够经过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!
2、 是不一样主域跨域;
虽然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', //这是定义在response.html的方法 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> // 通用方法 ajax请求 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); } // 通用方法 获取url参数 function _getQuery(key) { var query = location.href.split('?')[1], value = decodeURIComponent(query.split(key + "=")[1].split("&")[0]); return value; } //向process.php发送ajax请求 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) {//通用方法,获取URL参数 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自适应高度的问题了。