實習不久接到一個任務,在網頁中嵌套另外一個工程的網頁。本覺得這是垂手可得的一件事情,結果被測試姐姐折騰得夠嗆。屢次和我談心說到這個高度固定致使iframe出現滾動條有多麼很差看,對於工程總體的影響有多麼惡劣。由於跨域的緣由,這個需求被拖了許久,真是很痛苦的一件事。最終在我離開公司以前搞定了這個單。html
這裏就把個人研究過程寫下來以供你們參考。segmentfault
首先須要瞭解一下何爲同域,何爲跨域:跨域
URL | 說明 | 是否容許通訊 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名下 | 容許 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js |
同一域名下不一樣文件夾 | 容許 |
http://www.a.com:8000/a.js http://www.a.com/b.js |
同一域名,不一樣端口 | 不容許 |
http://www.a.com/a.js https://www.a.com/b.js |
同一域名,不一樣協議 | 不容許 |
http://www.a.com/a.js http://70.32.92.74/b.js |
域名和域名對應ip | 不容許 |
http://www.a.com/a.js http://script.a.com/b.js |
主域相同,子域不一樣 | 不容許 |
http://www.a.com/a.js http://a.com/b.js |
同一域名,不一樣二級域名(同上) | 不容許 |
http://www.cnblogs.com/a.js http://www.a.com/b.js |
不一樣域名 | 不容許 |
在最開始,因爲沒法完成跨域的高度自適應,我只能放棄跨域的狀況,只是作了在同域下的高度自適應,就是表格中第一種和第二種狀況。瀏覽器
在同域前提下,如下代碼可得到iframe頁面的window:服務器
document.getElementById("myframe").contentWindow
得到子頁面的window後便可在父頁面js操做子頁面。若跨域,則此時瀏覽器會報錯。cookie
子頁面得到父頁面window:app
parent.window
可是僅僅如此並不能得到測試姐姐的承認,後來又仔細研究了跨域的高度自適應。dom
其原理並不複雜,在父頁面嵌套子頁面,在子頁面嵌套與父頁面同域的代理頁面,由於父頁面與代理頁面同域,故父頁面與代理頁面可實現通訊。post
子頁面將自身高度定時賦值到代理頁面的URL後,代理頁面拿到自身URL中的數據傳遞給父頁面,父頁面改變子頁面所在iframe高度。從而達到iframe的高度跨域自適應。測試
說得比較繞,如下爲原理圖:
圖片來自這裏。
固然,要作到跨域高度自適應還有一個前提,你必須能操做子頁面,也就是說子頁面也是你能控制的,如果想把baidu.com做爲子頁面而達到高度自適應這個就沒法實現了。
在子頁面加入如下代碼:
/** * midway 頁面高度自適應代碼 * 在父頁面iframe的src地址後加上midway_url做爲線索 * 一可實現功能,二亦是實現動態加載js,實現此功能的通用 * midway_url 爲須要嵌入目標頁面實現功能的js地址 */ ;(function() { var str_midway_url = "midway_url"; //從URL中獲取midway_url var midway_reg = new RegExp(str_midway_url + "=([^&]*)(&|$)"); var midway_res = window.location.search.match(midway_reg); //從Cookie中獲取midway_url var cookies = document.cookie; var offset = cookies.indexOf(str_midway_url); //若URL中存在,則得到並存入Cookie //若Cookie中存在,則獲取 var midway_url = ''; if (midway_res) { midway_url = unescape(midway_res[1]); document.cookie = str_midway_url + "=" + midway_url; } else if (offset != -1) { offset += str_midway_url.length + 1; var end = cookies.indexOf(";", offset); end = (end != -1) ? end : cookies.length; midway_url = cookies.substring(offset, end); } //將目標地址的js引入頁面中,實現功能 if (midway_url) { var dom_script = document.createElement("script"); dom_script.src = midway_url; document.body.appendChild(dom_script); }; })();
父頁面經過iframe標籤引入子頁面, 在子頁面的URL後加上額外的一個參數midway_url。在子頁面的js中加入以上代碼,其核心代碼爲一個判斷語句:若url或者cookie中存在此參數,則繼續下一步,若不存在則再也不繼續。
通常關於iframe跨域的文章都是在子頁面直接經過iframe引入代理頁面,這樣作有幾個弊端:
經過URL參數將midway.js傳至子頁面,子頁面以此判斷本身被是被引用的。子頁面建立<script>標籤引入midway.js。
某些頁面由於某些關係須要自我刷新,使得URL中的參數丟失,故將midway_url存入cookie中持久化保存。
被引入子頁面的midway.js爲如下內容:
/** * 這是被子頁面引用的js - midway.js * midway.js 必須與 midway.html 在同一目錄 */ ;(function() { var midway = { init: function() { var str_midway_id = "midwayAgentPage"; var midway_url = this.getMidwayUrl(); this.createMidwayIfr(str_midway_id, midway_url); this.setHeight2Ifr(str_midway_id, midway_url); }, //在子頁面中建立代理iframe,其指向與父頁面同域的midway.html createMidwayIfr: function(str_midway_id, midway_url) { var midway_ifr = document.createElement("iframe"); midway_ifr.id = str_midway_id; midway_ifr.src = midway_url; midway_ifr.style.display = "none"; document.body.appendChild(midway_ifr); }, //定時將目標高度值附加到代理Iframe的src地址#以後 setHeight2Ifr: function(str_midway_id, midway_url) { setInterval(function() { //此處獲取目標高度,不宜直接取body高度,應在內容以外套一層div置於body內 var target_height = document.getElementsByTagName("div")[0].scrollHeight; var midway_ifr = document.getElementById(str_midway_id); if (midway_ifr) { midway_ifr.src = midway_url + '#' + target_height; } }, 66); }, //從cookie中獲得midway_url return http://.../midway.html getMidwayUrl: function() { var str_midway_url = "midway_url"; var cookies = document.cookie; var offset = cookies.indexOf(str_midway_url); var midway_url = ''; if (offset != -1) { offset += str_midway_url.length + 1; var end = cookies.indexOf(";", offset); end = (end != -1) ? end : cookies.length; midway_url = cookies.substring(offset, end); } if (midway_url) { //原midway_url爲...midway.js,此處改成...mindway.html return midway_url.slice(0, midway_url.lastIndexOf("/")) + "/midway.html"; } } }; midway.init(); })();
midway.js的主要功能在於根據midway_url,在子頁面建立iframe引入midway.html,定時將子頁面的實際高度賦值到iframe的src以後。
子頁面中被引入的midway.html的代碼以下:
<!DOCTYPE html> <html> <head> <title>Midway</title> </head> <body> <script> window.onload = function() { //得到父頁面中的iFrame var ifr = parent.parent.document.getElementById('myframe'); //定時從url中獲取到目標高度 //並賦予目標iFrame相應高度,實現自適應 setInterval(function() { var h = location.hash ? location.hash.substring(1) : 0; if(h) ifr.style.height = h + 'px'; }, 66); }; </script> </body> </html>
midway.html至關於一箇中轉站,因爲midway.html與父頁面同域可互相通訊,故經過midway.html實時改變父頁面中iframe的高度。
父頁面中關於iFrame的代碼以下:
<iframe id="myframe" name="myframe" width=100% height=100% frameborder=0 marginheight=0 marginwidth=0 scrolling="no"> </iframe> <script> ;(function(){ //此處爲midway.js的URL //注意:midway.js與midway.html應在同一文件夾 var midway_url = "http://.../midway.js"; //此處將midway.js地址添加到目標頁面的URL後 document.getElementById("myframe").src("http://...&midway_url=" + midway_url); })(); </script>
雖然是比較繞,但實現了測試妹妹須要的功能,也算是完美解決了任務。
問題解決的同時,也作了其餘的一些研究,也一併寫在下面。
如果在同一主域不一樣子域的狀況下,例如news.a.com與a.com或者news.a.com與blog.a.com之間,能夠經過js對於主域的設置來實現相互通訊:
document.domain = "a.com"
如此設置即可使用第一種方法實現兩個頁面的自由通訊。
window.name 屬性可設置或返回存放窗口的名稱的一個字符串。利用這個屬性作iframe跨域傳值傳值是很方便的。
具體操做參看這裏。
其原理與midway的類似,midway利用一個與父頁面同域的代理頁面,經過改變其location.hash實現跨域傳值,而這裏這是經過改變window.name實現跨域傳值,本質上都是經過代理iframe做爲中介來實現的。
location.hash能傳遞的數據很是有限。
window.name能夠傳遞更多的數據(大小通常爲2M,IE和firefox下能夠大至32M左右),數據格式可自定義(JSON)。
有空再談。
參考:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/name
https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/hash