下面本人來談談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自適應高度的問題了。