如何獲取跨域iframe高度

weixin_33751566發表於2017-05-20

【技術研究】如何動態獲取跨域iframe高度

引言

iframe是一個“好東西”,但是又會帶給你很多頭疼的“問題”,特別是在ios的相容性問題。在ios當中,iframe裡的頁面不會隨著外層的網頁大小自適應彈性縮放。相比之下,PC瀏覽器瀏覽器和安卓的瀏覽器則是可以實現縮放,這導致了差異性。這時候第一時刻,想到的是相容的寫法。專門針對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);
}

上述的相容寫法能解決部分網站的問題,但是如果是響應�式網頁,頁面跳動的情況還是會出現問題的。

隨著技術的發展,iframe一般都是萎了滿足跨域的頁面。受限於瀏覽器的同源政策,父頁面是沒法跨域獲取子網頁的高度或者寬度。這時候,我們可能考慮將iframe的高和寬“定死”。跨域交換iframe內外的資料的方法有以下兩種,一種是中間代理頁面,一種是h5的API--postMessage。

中間代理頁面

參考iframe高度自適應的6個方法的最後一種方法,這種方法是建立了在兩個頁面中一箇中間代理層。原理很簡單,用代理層網頁地址的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把高度和寬度值組織好到代理頁面的連結。由於連結的呼叫不受跨域的限制,也算是走了個“後門”,把你想要的值“偷偷”傳到代理頁面上。而代理頁面和主頁面同源,不構成跨域,所以避免了瀏覽器的跨域限制。我們還需要在iframe目標頁面新增一段程式碼,就是把新增一個iframe把資料往連結上拼接。在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的發展,postMessage是不同的html頁面之間進行資料通訊的方法,大大簡化了上述方法的步驟。

在b.html中新增一段程式碼,在它載入完成後,往父頁面跨域傳送自己的高和寬,並且可以限制你傳送的父網站的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網站新增一個事件監聽,獲取iframe內的b.html傳送的高與寬,從而設定父頁面的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

總結

相比之下,第二種方法會比較簡單和有效。但是由於跨域限制,你不得不要求對方新增一段程式碼去“消除”跨域限制,也是出於安全性不得已的實現方法。

總的來說,iframe在移動端受非常多的限制,儘可能地慎用。

685800-b90086f21952919c.jpg
微信公眾號

原部落格地址

團隊GITHUB

相關文章