關於iframe跨域實踐


提要

項目中與到iframe子頁面中需要通過top獲取在父頁面中的全局變量的需求,由於App部署的緣故,導致父頁面和iframe子頁面分別在不同的端口下,導致iframe跨域現象,通過查閱資料進行問題解決。

瀏覽器有一個同源策略,第一種限制就是不能通過ajax的方法去請求不同源的文檔。第二種限制是不能瀏覽器中不同域的框架之間是不能進行js的交互操作的。

不過有一點,不同框架之間(父子框架和同輩框架),是能夠獲取到彼此的window對象的,但是卻不能獲取到window對象的屬性和方法(html5中的的postMessage方法是一個例外,還有些瀏覽器比如ie6也可以使用top、parent等少數幾個屬性),總之,你可以當做是只能獲取到一個幾乎無用的window對象。比如,有一個頁面,它的地址是http://wwww.example.com/a.html, 在這個頁面里邊有一個iframe,它的src是http://example.com/b.html,很顯然,這個頁面與它里邊的iframe框架是不同域的,所以我們是無法通過在頁面中書協js代碼來獲取iframe中的東西的,同樣iframe中的內容也無法直接獲取到a.html中的內容。

【以上內容來自無雙的博客】

實踐

根據博客中介紹的幾種方式進行實踐:

通過修改document.domain來跨子域

子域中無法獲取父域的數據的時候就可以利用document.domain都設置成相同的域名就可以完成。但是要注意的是,document.domain的設置是有限制的,我們把document.domain設置成自身或者更高一級的父域,且主域必須相同。例如:a.b.example.com中某個文檔的document.domaiin可以設成a.b.example.comb.example.com、example.com中的任意一個,但是不可以設成c.a.b.example.com,因為這是當前作用於的子域,也不能設成baidu.com,因為主域已經不相同了。

這種方式用來獲取端口不同的跨域處理起來是很方便的:

//父域的運行環境是http://localhost:8087/
//同樣在部署在同一台服務器上的不同端口的應用也是適用的

<iframe src="http://localhost:8086/" id="iframepage" width="100%" height="100%" frameborder="0" scrolling="yes" onLoad="getData"></iframe>

<script>
    window.parentDate = {
        "name": "hello world!",
        "age": 18
    }
    /**
     * 使用document.domain解決iframe父子模塊跨域的問題
     */
    let parentDomain = window.location.hostname;
    console.log("domain",parentDomain); //localhost
    document.domain = parentDomain;
</script>
//子域的運行環境是http://localhost:8086/

<script>
    /**
     * 使用document.domain解決iframe父子模塊跨域的問題
     */
    console.log(document.domain); //localhost
    let childDomain = document.domain;
    document.domain = childDomain;
    let parentDate = top.parentDate;
    console.log("從父域獲取到的數據",parentDate);   
    // 此處打印數據為
    // {
    //     "name": "hello world!",
    //     "age": 18
    // }
</script>

到這里就能夠把主域的數據傳遞給子域了。同樣也可以把子域的數據傳遞子域:

//子域的獲取到top之后給top上添加屬性

<script>
    let childDomain = document.domain;
    document.domain = childDomain;

    top.childData = {   //獲取到top之后給top添加屬性
        "name": "你好世界!",
        "age": 26
    }
</script>
//父域在iframe加載完成之后就可以獲取到子域添加的屬性

<iframe src="http://localhost:8086/" id="iframepage" width="100%" height="100%" frameborder="0" scrolling="yes" onLoad="getData"></iframe>

<script>
    getData(){
        console.log("子域傳遞給父域的數據",top.childData);
        // 此處打印數據
        // {
        //      "name": "你好世界!",
        //      "age": 26
        // }
    }
</script>

這樣就可以完成父子組件之間的通訊了。

不過如果你想在http://www.example.com/a.html頁面中通過ajax直接請求http://example.com/b.html頁面,即使你設置了相同的document.domain也還是不行的,所以修改document.domain的方法只適用於不同子域的框架間的交互。

使用HTML5中新引入的window.postMessage方法來跨域傳遞數據

window.postMessage(message, targetOrgin)方法是html5新引進的特性,可以使用它來想其他的window對象發送消息,無論這個window對象是屬於同源或者不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。

調用postMessage方法的window對象是指要接受消息的哪一個window對象,該方法的第一個參數message為要發送的消息,類型只能為字符串;第二個參數targetOrgin用來限定接收消息的那個window對象所在的域,如果不想限定域,可以使用通配符*。

需要接收消息的window對象,可是通過監聽自身的message時間來獲取傳過來的消息,消息內容存儲在該事件對象的data屬性中。

上面所有的向其他window對象發送消息,其中就是指一個頁面有幾個框架的那種情況,因為每一個框架都有一個window對象。在討論第二種方法的時候,我們說過,不同域的框架間是可以獲取到對象的window對象的,而且也可以使用window.postMessage這個方法。

//父域的運行環境是http://localhost:8087/

<iframe src="http://127.0.0.1:8086/" id="iframepage" width="100%" height="100%" frameborder="0" scrolling="yes" onLoad="getData"></iframe>

<script>
    getData(){
        let iframe = document.getElementById('iframepage');
        let win = iframe.contentWindow;
        win.postMessage(JSON.stringify(parentDate),"*");
    }
</script>
//子域的運行環境是http://127.0.0.1:8086/

    /**
     * 使用postMessage解決iframe父子模塊跨域的問題
     */
    window.onmessage = function(e){
        e = e || event;
        console.log("從父域獲取到的數據",JSON.parse(e.data));
        // 此處打印的數據為
        // {
        //     "name": "hello world!",
        //     "age": 18
        // }
    }

這樣在任何一個域內都可以獲取到從父域傳遞的數據。通過postMessage來跨域傳遞數據還是比較直觀和方便的,但是缺點是IE6、IE7不支持,至於能不能用可以在 Can I use上進行驗證。目前看到的是IE11是部分支持,不過剛才的方法在IE11上驗證是能夠正常執行的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM