【技術(shù)研究】如何動態(tài)獲取跨域iframe高度
引言
iframe是一個“好東西”,但是又會帶給你很多頭疼的“問題”,特別是在ios的兼容性問題。在ios當中,iframe里的頁面不會隨著外層的網(wǎng)頁大小自適應彈性縮放。相比之下,PC瀏覽器瀏覽器和安卓的瀏覽器則是可以實現(xiàn)縮放,這導致了差異性。這時候第一時刻,想到的是兼容的寫法。專門針對ios專門設置iframe的scrolling屬性為“no”,其他瀏覽器為“yes”,如下方源碼。但是如果iframe子頁面中存在響應式部件tab,高度進行變化,則會引起重繪重排,導致頁面突然跳到頂部。
<div id="url-wrapper"></div>
html, body{
height: 100%;
}
#url-wrapper{
margin-top: 51px;
height: 100%;
}
#url-wrapper iframe{
height: 100%;
width: 100%;
}
#url-wrapper.ios{
overflow-y: auto;
-webkit-overflow-scrolling:touch !important;
height: 100%;
}
#url-wrapper.ios iframe{
height: 100%;
min-width: 100%;
width: 100px;
*width: 100%;
}
function create_iframe(url){
var wrapper = jQuery('#url-wrapper');
if(navigator.userAgent.match(/(iPod|iPhone|iPad)/)){
wrapper.addClass('ios');
var scrolling = 'no';
}else{
var scrolling = 'yes';
}
jQuery('<iframe>', {
src: url,
id: 'url',
frameborder: 0,
scrolling: scrolling
}).appendTo(wrapper);
}
上述的兼容寫法能解決部分網(wǎng)站的問題,但是如果是響應式網(wǎng)頁,頁面跳動的情況還是會出現(xiàn)問題的。
隨著技術(shù)的發(fā)展,iframe一般都是萎了滿足跨域的頁面。受限于瀏覽器的同源政策,父頁面是沒法跨域獲取子網(wǎng)頁的高度或者寬度。這時候,我們可能考慮將iframe的高和寬“定死”。跨域交換iframe內(nèi)外的數(shù)據(jù)的方法有以下兩種,一種是中間代理頁面,一種是h5的API--postMessage。
中間代理頁面
參考iframe高度自適應的6個方法的最后一種方法,這種方法是建立了在兩個頁面中一個中間代理層。原理很簡單,用代理層網(wǎng)頁地址的hash值傳高度和寬度。假設www.a.com域名下的一個頁面a.html要包含www.b.com下的一個頁面b.html。這時,我們需要在a域名下添加一個agent.html,代理層的代碼如下,放置在自己的服務器。
//agent.html
<script type="text/javascript">
var other = window.parent.parent.document.getElementById("other");
var hash_url = window.location.hash;
if (hash_url.indexOf("#") >= 0) {
var hash_width = hash_url.split("#")[1].split("|")[0] + "px";
var hash_height = hash_url.split("#")[1].split("|")[1] + "px";
other.style.width = hash_width;
other.style.height = hash_height;
}
</script>
而它是被iframe目標頁面所引用,iframe把高度和寬度值組織好到代理頁面的鏈接。由于鏈接的調(diào)用不受跨域的限制,也算是走了個“后門”,把你想要的值“偷偷”傳到代理頁面上。而代理頁面和主頁面同源,不構(gòu)成跨域,所以避免了瀏覽器的跨域限制。我們還需要在iframe目標頁面添加一段代碼,就是把添加一個iframe把數(shù)據(jù)往鏈接上拼接。在b.html的尾部加上這段js。
//b.html
(function autoHeight() {
var b_width = document.body.clientWidth;
var b_height = document.body.clientHeight;
var agent = document.getElementById("agent");
agent.src = agent.src + "#" + b_width + "|" + b_height; // 這里通過hash傳遞b.htm的寬高
})();
而在a.html還是原封不動的那個iframe就可以了。
<!--a.html-->
<iframe src="./othersite.html" id="other" frameborder="0" scrolling="no" style="border:0px;"></iframe>
源碼參考brandonxiang/iframe-height。
postMessage
有些人覺得上面的方法非常難理解,因為中間代理層的緣故,增加了請求量,影響了加載效率。
隨著HTML5 API的發(fā)展,postMessage是不同的html頁面之間進行數(shù)據(jù)通信的方法,大大簡化了上述方法的步驟。
在b.html中添加一段代碼,在它加載完成后,往父頁面跨域發(fā)送自己的高和寬,并且可以限制你發(fā)送的父網(wǎng)站的ip地址,大大保證安全性。
//b.html
document.addEventListener('DOMContentLoaded', function () {
var tbody = document.body
var width = tbody.clientWidth
var height = tbody.clientHeight
window.parent.postMessage({ height: height, width: width }, '*')
}, false)
還需要在a.html網(wǎng)站添加一個事件監(jiān)聽,獲取iframe內(nèi)的b.html發(fā)送的高與寬,從而設置父頁面的iframe的高與寬。
//a.html
var frame = document.getElementById('other')
window.addEventListener('message', function(e){
frame.style.height = e.data.height+'px'
frame.style.width = e.data.width+'px'
})
源碼參考brandonxiang/iframe-height。
總結(jié)
相比之下,第二種方法會比較簡單和有效。但是由于跨域限制,你不得不要求對方添加一段代碼去“消除”跨域限制,也是出于安全性不得已的實現(xiàn)方法。
總的來說,iframe在移動端受非常多的限制,盡可能地慎用。