JS解決iframe之間通信和自適應(yīng)高度的問(wèn)題
首先說(shuō)明下,iframe通信 分為:同域通信 和 跨域通信。
一、 同域通信
所謂同域通信是指 http://localhost/demo/iframe/iframeA.html 下的a.html頁(yè)面嵌套 iframe
比如: <iframe src="http://localhost/demo/iframe/iframeB.html" id="iframeA" name="iframeA">的B.html頁(yè)面,這兩個(gè)頁(yè)面數(shù)據(jù)進(jìn)行通信,比如我想在父頁(yè)面A.html 調(diào)用子頁(yè)面當(dāng)中的函數(shù) 我們很容易想到或者google下 document.getElementById('iframeA').contentWindow.b();
這種方法,其中b 是子頁(yè)面B.html中的一個(gè)函數(shù)。但是這樣調(diào)用下有個(gè)問(wèn)題我糾結(jié)了很久,就是既然在火狐下報(bào)這樣的錯(cuò)誤, 如下:
b不是個(gè)函數(shù) 但是我在子頁(yè)面明明定義了這么一個(gè)函數(shù),那么為什么會(huì)報(bào)這樣的錯(cuò)誤呢?經(jīng)過(guò)仔細(xì)分析及google,發(fā)現(xiàn)有這么一個(gè)問(wèn)題需要理解,當(dāng)iframe沒(méi)有加載完成后 我就去執(zhí)行這個(gè)js會(huì)報(bào)這樣的錯(cuò)誤,所以就試著在火狐下 用iframe.onload
這個(gè)函數(shù) 進(jìn)行測(cè)試,果然沒(méi)有報(bào)錯(cuò),是正確的 所以就確定是這個(gè)問(wèn)題。所以就想寫個(gè)兼容IE和火狐 google寫個(gè)函數(shù) 來(lái)確定iframe已經(jīng)加載完成!,其實(shí)給個(gè)回調(diào)函數(shù)來(lái)調(diào)用我們上面的方法。
綜合上面的思路 就可以寫個(gè)這樣的代碼:
<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"); }
子頁(yè)面調(diào)用父頁(yè)面的函數(shù)很簡(jiǎn)單,只要這樣搞下就ok了,window.parent.A();
子頁(yè)面取父頁(yè)面元素的值: window.parent.document.getElementById("topName").innerHTML
等方法。
二: iframe跨域通信。
iframe跨域訪問(wèn)一般分為2種情況,第一種是同主域,不同子域的跨域。 第二種是:不同主域跨域。
1、 是同主域下面,不同子域之間的跨域;可以通過(guò)document.domain 來(lái)設(shè)置相同的主域來(lái)解決。
假如現(xiàn)在我有個(gè)域 abc.example.com 下有個(gè)頁(yè)面叫abc.html, 頁(yè)面上嵌套了一個(gè)iframe 如下:<iframe src="http://def.example.com/demo/def.html" id="iframe2" style="display:none;"></iframe>,我想在abc域下的頁(yè)面abc.html 訪問(wèn) def域下的def.html 我們都知道由于安全性 游覽器的同源策略的限制,js不能操作頁(yè)面不同域下 不同協(xié)議下 不同端口的頁(yè)面,所以就要解決跨域訪問(wèn)了,假如父頁(yè)面abc.html 頁(yè)面有個(gè)js函數(shù):function test(){console.log(1);};
我想在子頁(yè)面調(diào)用這個(gè)函數(shù) 還是按照上面的同域方式調(diào)用 parent.test();
這樣,通過(guò)在火狐下看 已經(jīng)跨域了 解決的辦法是 在各個(gè)js函數(shù)頂部 加一句 document.domain = 'example.com'
,就可以解決了。
abc.html代碼如下:
<iframe src="http://def.example.com/demo/def.html" id="iframe2" style="display:none;"></iframe> // 跨域 子頁(yè)調(diào)用父頁(yè)的 函數(shù) (假設(shè)是下面test函數(shù)) document.domain = 'example.com'; function test(){console.log(1);};
def.html代碼如下:
/* * 子頁(yè)調(diào)用父頁(yè)的方法 */ document.domain = 'example.com'; //window.top.test(); window.parent.test();
還是這兩個(gè)頁(yè)面 我想父頁(yè)調(diào)用子頁(yè) 如下方法:
a.html代碼如下:
/* * 跨域 父頁(yè)想調(diào)用子頁(yè)的的函數(shù) */ 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(); } } }
假如現(xiàn)在def.html頁(yè)面有個(gè)child函數(shù) 代碼如下:
document.domain = 'example.com'; function child(){console.log('我是子頁(yè)');}
就可以跨域調(diào)用了 不管是子頁(yè)面調(diào)用父頁(yè)面 還是父頁(yè)面調(diào)用子頁(yè)面。一切ok!
2、 是不同主域跨域;
雖然google有幾種方法關(guān)于不同主域上的跨域問(wèn)題 有通過(guò)location.hash
方法或者window.name
方法或者h(yuǎn)tml5及flash等等,但是我覺(jué)得下面iframe這種方法值得學(xué)習(xí)下,
如下圖所示:域a.com的頁(yè)面request.html(即http://a.com/demo/ajax/ajaxproxy/request.html)里面嵌套了一個(gè)iframe指向域b.com(http://b.com/demo/ajax/ajaxproxy/response.html)的response.html,而response.html里又嵌套了域a.com的proxy.html。
思路:要實(shí)現(xiàn)a.com域下的request.html頁(yè)面請(qǐng)求域b.com下的process.php,可以將請(qǐng)求參數(shù)通過(guò)url傳給response.html,由response.html向process.php發(fā)起真正的ajax請(qǐng)求(response.html與process.php都屬于域b.com),然后將返回的結(jié)果通過(guò)url傳給proxy.html,最后由于proxy.html和request.html是在同個(gè)域下,所以可以在proxy.html利用window.top 將結(jié)果返回在request.html完成真正的跨域。
ok, 先看看頁(yè)面結(jié)構(gòu)
a.com域下有:
request.html
proxy.html
b.com域下有:
response.html
process.php
先來(lái)看看request.html頁(yè)面如下:
<!DOCTYPE HTML> <html> <head> <title> New Document </title> </head> <body> <p id="result">這里將會(huì)填上響應(yīng)的結(jié)果</p> <a id="sendBtn" href="javascript:void(0)">點(diǎn)擊,發(fā)送跨域請(qǐng)求</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}', //這是請(qǐng)求的參數(shù) callback = "CallBack"; //這是請(qǐng)求全過(guò)程完成后執(zhí)行的回調(diào)函數(shù),執(zhí)行最后的動(dòng)作 CrossRequest(url, fn, reqdata, callback); //發(fā)送請(qǐng)求 } function CrossRequest(url,fn,reqdata,callback) { var server = document.getElementById('serverIf'); server.src = url + '?fn=' +encodeURIComponent(fn) + "&data=" +encodeURIComponent(reqdata) + "&callback="+encodeURIComponent(callback); } //回調(diào)函數(shù) 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>
這個(gè)頁(yè)面其實(shí)就是要告訴response.html:我要讓你執(zhí)行你定義好的方法GetPerson
,并且要用我給你的參數(shù)'{"id" : 24}'。response.html純粹是負(fù)責(zé)將CallBack
這個(gè)方法名傳遞給下一位仁兄proxy.html,而proxy.html拿到了CallBack
這個(gè)方法名就可以執(zhí)行了,因?yàn)閜roxy.html和request.html是同域的。
response.html代碼如下:
<!DOCTYPE HTML> <html> <head> <title> New Document </title> </head> <body> <iframe id="proxy"></iframe> <script> // 通用方法 ajax請(qǐng)求 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參數(shù) function _getQuery(key) { var query = location.href.split('?')[1], value = decodeURIComponent(query.split(key + "=")[1].split("&")[0]); return value; } //向process.php發(fā)送ajax請(qǐng)求 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>
這里其實(shí)就是接收來(lái)自request.html的請(qǐng)求得到請(qǐng)求參數(shù)和方法后向服務(wù)器process.php發(fā)出真正的ajax請(qǐng)求,然后將從服務(wù)器返回的數(shù)據(jù)以及從request.html傳過(guò)來(lái)的回調(diào)函數(shù)名傳遞給proxy.html?!?/p>
接下來(lái)看看php代碼如下,其實(shí)就是想返回一個(gè)json數(shù)據(jù):
<?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參數(shù) 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透過(guò)response.html傳過(guò)來(lái)的回調(diào)函數(shù)名以及從response.html直接傳過(guò)來(lái)的響應(yīng)數(shù)據(jù),利用window.top執(zhí)行request.html里定義的回調(diào)函數(shù)。
三、iframe高度自適應(yīng)的問(wèn)題。
iframe高度自適應(yīng)分為2種,一種是同域下自適應(yīng) 另外一種是跨域下自適應(yīng),下面我們來(lái)看看同域下iframe高度自適應(yīng)的問(wèn)題。
1.同域下iframe高度自適應(yīng)的問(wèn)題:
思路:獲取被嵌套iframe元素,通過(guò)JavaScript取得被嵌套頁(yè)面最終高度,然后在主頁(yè)面進(jìn)行設(shè)置來(lái)實(shí)現(xiàn)。
假如我們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>
就可以動(dòng)態(tài)設(shè)置iframe1頁(yè)面的高度為iframe2的高度了。
2. 跨域下iframe高度自適應(yīng)的問(wèn)題。
首先我們知道iframe跨域我們是不能用上面js方式來(lái)控制了,所以我們只能用個(gè)中間鍵 我們可以在a.com域下iframe1.html頁(yè)面嵌套一個(gè)b.com域下的iframe2.html頁(yè)面,然后我在iframe2.html頁(yè)面嵌套個(gè)和iframe1.html相同域的iframe3.html頁(yè)面了,這樣的話 iframe1.html和iframe3.html就可以無(wú)障礙的進(jìn)行通信了,因?yàn)轫?yè)面iframe2.html嵌套iframe3.html,所以iframe2.html可以改寫iframe3.html的href值。
iframe1中的內(nèi)容:
iframe1.html內(nèi)容主要接受iframe3.html頁(yè)面?zhèn)鬟^(guò)來(lái)的內(nèi)容并且去完成相應(yīng)的操作。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中的內(nèi)容:
iframe2.html內(nèi)容是怎么把值傳給iframe3.html頁(yè)面,剛才說(shuō)了是將值傳遞到iframe3.html頁(yè)面的href中,所以只要修改iframe的src就可以,因?yàn)椴挥盟⑿翪頁(yè)面,所以可以用過(guò)hash的方式傳遞給iframe3.html頁(yè)面.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>
可以看到 默認(rèn)情況下 iframe1.html 頁(yè)面我給iframe2.html的高度是200像素 但是在iframe2.html我給iframe3.html高度是230像素,那么正常情況下是有滾動(dòng)條的,那么現(xiàn)在我是想在iframe2.html獲取滾動(dòng)條的高度,把高度傳給通過(guò)iframe3.html的src里面去,然后在iframe3.html頁(yè)面里獲取這個(gè)高度值 傳給iframe1.html(因?yàn)閕frame1.html和iframe3.html是同域的),所以iframe1.html能取到這個(gè)高度值,再設(shè)置下本身的高度就是這個(gè)值就ok了。
iframe3.html頁(yè)面的唯一功能就是接收iframe2.html頁(yè)面通過(guò)href傳進(jìn)來(lái)的值并且傳遞給iframe1.html頁(yè)面,可到iframe2.html頁(yè)面?zhèn)鱽?lái)的值可以通過(guò)一個(gè)定時(shí)器不停去查看location.href是 否被改變,但是這樣感覺(jué)效率很低,還有個(gè)方式就是在新的瀏覽器中通過(guò)onhashchange
事件 (IE8+,Chrome5.0+,Firefox3.6+,Safari5.0+,Opera10.6+)來(lái)監(jiān)聽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>
這樣就可以解決通過(guò)跨域?qū)崿F(xiàn)iframe自適應(yīng)高度的問(wèn)題了。
四、總結(jié)
以上就是本文的全部?jī)?nèi)容了,希望對(duì)大家的學(xué)習(xí)工作能有所幫助。如果有疑問(wèn)可以留言討論。
相關(guān)文章
基于原生js運(yùn)動(dòng)方式關(guān)鍵點(diǎn)的總結(jié)(推薦)
下面小編就為大家?guī)?lái)一篇基于原生js運(yùn)動(dòng)方式關(guān)鍵點(diǎn)的總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10小程序從手動(dòng)埋點(diǎn)到自動(dòng)埋點(diǎn)的實(shí)現(xiàn)方法
這篇文章主要介紹了小程序從手動(dòng)埋點(diǎn)到自動(dòng)埋點(diǎn)的實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01JS獲取屏幕,瀏覽器窗口大小,網(wǎng)頁(yè)高度寬度(實(shí)現(xiàn)代碼)
本篇文章主要介紹了JS獲取屏幕,瀏覽器窗口大小,網(wǎng)頁(yè)高度寬度的實(shí)現(xiàn)代碼。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12egg.js的基本使用和調(diào)用數(shù)據(jù)庫(kù)的方法示例
這篇文章主要介紹了egg.js的基本使用和調(diào)用數(shù)據(jù)庫(kù)的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Javascript模仿淘寶信用評(píng)價(jià)實(shí)例(附源碼)
這篇文章主要介紹了Javascript模仿淘寶信用評(píng)價(jià)功能實(shí)現(xiàn)方法,以完整實(shí)例形式分析了JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)改變頁(yè)面元素的相關(guān)技巧,并附帶了完整的實(shí)例代碼供讀者下載參考,需要的朋友可以參考下2015-11-11uniapp項(xiàng)目使用防抖及節(jié)流的方案實(shí)戰(zhàn)
防抖就是指觸發(fā)事件后把觸發(fā)非常頻繁的事件合并成一次去執(zhí)行,節(jié)流是指頻繁觸發(fā)事件時(shí)只會(huì)在指定的時(shí)間段內(nèi)執(zhí)行事件回調(diào),即觸發(fā)事件間隔大于等于指定的時(shí)間才會(huì)執(zhí)行回調(diào)函數(shù),這篇文章主要給大家介紹了關(guān)于uniapp項(xiàng)目使用防抖及節(jié)流的相關(guān)資料,需要的朋友可以參考下2023-01-01微信小程序首頁(yè)的分類功能和搜索功能的實(shí)現(xiàn)思路及代碼詳解
這篇文章主要介紹了微信小程序首頁(yè)的分類功能和搜索功能的實(shí)現(xiàn)思路及代碼詳解,微信宣布了微信小程序開發(fā)者工具新增“云開發(fā)”功能,現(xiàn)在無(wú)需服務(wù)器即可實(shí)現(xiàn)小程序的快速迭代,感興趣的朋友跟隨小編一起看看吧2018-09-09Javascript ParentNode和ChildNode接口原理解析
這篇文章主要介紹了Javascript ParentNode和ChildNode接口原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03