iframe內嵌及跨域通信(iframe跨域 內嵌網頁 iframe刷新重載 postMessage 事件監聽)


iframe內嵌及跨域通信

iframe跨域 內嵌網頁 iframe刷新重載 postMessage 事件監聽

 

前言

對於iframe標簽,現在都應該用的很少了因為它存在一些問題,比如安全問題或者能耗高,但最近筆者就使用了它做網頁內嵌並跨域處理了數據,所以記錄記錄。

iframe基本概念

<iframe src="demo.html" height="300" width="500" name="demo" scrolling="auto" sandbox="allow-same-origin"></iframe>

 

iframe的一些基本屬性:

src iframe頁面地址,有同域跨域之分

height iframe高度

width iframe寬度

name iframe命名,可通過window.frames[xxx]被調用

scrolling iframe滾動模式

sandbox html5新特性,用於限制iframe的功能

使用iframe的正確姿勢

可以通過以下 選擇器來獲取iframe節點(window.frames['xxx']的方式好像已經不能用了):

 

 1 document.getElementById('iframeId')
 2 
 3 document.getElementsByName('iframeName')
 4 
 5 document.getElementsByClassName('iframeClassName')
 6 
 7 document.getElementsByTagName('iframe')
 8 
 9 document.querySelector('#iframeId')
10 
11 document.querySelector('.iframeClassName')

 

 

我們可以通過contentWindow和contentDocument兩個API獲取iframe的window對象和document對象。

 

1 let iwindow = iframe.contentWindow; // 獲取iframe的window對象
2 
3 let idoc = iframe.contentDocument; // 獲取iframe的document對象

 

 

iframe使用父級內容的正確姿勢

我們通過window.self,window.parent,window.top這三個屬性分別獲取自身window對象,父級window對象,頂級window對象。

 

看圖說話

  

 

所以:

iframe1.self === iframe1

iframe1.parent === iframe2

iframe2.parent === window

iframe1.top === window

 

同域/跨域

什么是同域什么跨域咧?同域跨域的區別在哪咧?我們一般會使用iframe來進行父子頁面的通信,但父子頁面是否同域決定了它們之間能否進行通信。

js遵循同源策略,即協議域名端口一致,否則都算跨域。

同源策略 是由Netscape提出的一個著名的安全策略,現在所有支持JavaScript 的瀏覽器都會使用這個策略。實際上,這種策略只是一個規范,並不是強制要求,各大廠商的瀏覽器只是針對同源策略的一種實現。它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。

跨域 簡單的來說,指的是兩個資源非同源。出於安全方面的考慮,頁面中的JavaScript在請求非同源的資源時就會出 跨域問題 ——即跨域請求,這時,由於同源策略,我們的請求會被瀏覽器禁止。也就出現了 我們常說的 跨域 問題。

 

通過這個圖可以進一步幫助我們理解同域和跨域。

 

 

iframe跨域通訊之document.domain

對於主域相同子域不同的兩個頁面,我們可以通過document.domain + iframe來解決跨域通信問題。

舉個🌰,網頁a(http://www.easonwong.com)和網頁b(http://script.easonwong.com),兩者都設置document.domain = 'easonwong.com'(這樣瀏覽器就會認為它們處於同一個域下),然后網頁a再創建iframe上網頁b,就可以進行通信啦!

 

網頁a

 1 document.domain = 'easonwong.com';
 2 
 3 var ifr = document.createElement('iframe');
 4 
 5 ifr.src = 'http://script.easonwong.com';
 6 
 7 ifr.style.display = 'none';
 8 
 9 document.body.appendChild(ifr);
10 
11 ifr.onload = function(){
12 
13     let doc = ifr.contentDocument || ifr.contentWindow.document;
14 
15     // 在這里操縱b.html
16 
17 };
18 
19  

 

網頁b

document.domain = 'easonwong.com';

 

iframe跨域通訊之postMessage

postMessage是html5的新特性,具體介紹看傳送門。

 

postMessage介紹

兼容性 IE8以上

我們可以通過html5這個新特性進行iframe間的跨域通信,使用postMessage進行數據傳遞,通過Message監聽通信事件。舉個🌰

 

網頁a

 1 document.domain = 'easonwong.com';
 2 
 3 var ifr = document.createElement('iframe');
 4 
 5 ifr.src = 'http://script.easonwong.com';
 6 
 7 ifr.style.display = 'none';
 8 
 9 document.body.appendChild(ifr);
10 
11 // 發送數據
12 
13 ifr.postmessage('hello, I`m a', 'http://script.easonwong.com');

 

 

網頁b

 1 // 監聽message事件
 2 
 3 window.addEventListener('message', receiver, false);
 4 
 5 function receiver(e) {
 6 
 7     if (e.origin == 'http://www.easonwong.com') {
 8 
 9         if (e.data == 'hello, I`m a') {
10 
11             e.source.postMessage('hello, I`m b', e.origin);信息
12 
13         }
14 
15     }
16 
17 }

 

 

iframe的安全問題

iframe小廣告

很讓我們討厭iframe的一點,就是很多網站都會有各種讓人防不勝防的小廣告,它們大多就是用通過iframe實現的,本來想點擊某個播放按鈕,結果直接跳到不知道去了哪個新世界去了。

所以我們一定要注意在用iframe的同時,要防止我們被iframe了。

 

防嵌套頁面操作

在前端領域,我們可以通過window.top來防止我們頁面被嵌套。

if(window != window.top){

    window.top.location.href = myURL;

}

 

或者通過window.location.host來檢測是否跨域了

if (top.location.host != window.location.host) {

  top.location.href = window.location.href;

}

而后端也可以做對應的防范措施,通過設置X-Frame-Options響應頭來確保自己網站的內容沒有被嵌到別人的網站中去,也從而避免了點擊劫持 (clickjacking) 的攻擊。

CSP

內容安全策略(CSP)用於檢測和減輕用於 Web 站點的特定類型的攻擊,例如 XSS 和數據注入等。

MDN CSP

通過CSP配置sandbox和child-src可以設置iframe的有效地址,它限制適iframe的行為,包括阻止彈出窗口,防止插件和腳本的執行,而且可以執行一個同源策略。

 

用法

我們可以在html頭部中加上<meta>標簽

<meta http-equiv="Content-Security-Policy" content="child-src 'unsafe-inline' 'unsafe-eval' www.easonwong.com">

或者通過HTTP頭部信息加上Content-Security-Policy字段

 

現在回頭來看看業務上的使用:

邏輯上簡單來說就是,用一個iframe標簽從a網頁跳到另一個b網頁,並且在b頁面上操作后將數據拿回a網頁,效果代碼如下:

HTML:

 1 <el-dialog title="選址"
 2 
 3       width = "70%"
 4 
 5       height = '500px'
 6 
 7       :visible.sync="addrSelectDialogVisible"
 8 
 9       :before-close="handleCloseAddrSelectDialog"
10 
11       :modal-append-to-body="false">
12 
13       <div class="addr-select-box">
14 
15         <!-- start of 詳情-->
16 
17         <iframe
18 
19         src="http://134.96.249.135:8088/ass/webLogin.do?areaCode=571" frameborder="0" width="100%" height="100%" ref="iframeId">
20 
21         </iframe>
22 
23         <!-- end of 詳情-->
24 
25         </div>
26 
27     </el-dialog>

 

 

 1 //注冊監聽選址的消息
 2 
 3       registerSelectAddrMessage( ){
 4 
 5         let self = this ;
 6 
 7         if (navigator.appName=="Microsoft Internet Explorer"&&(navigator.appVersion.match(/8./i)=="8."||navigator.userAgent.indexOf("MSIE 8.0")>0||navigator.appVersion.match(/7./i)=="7.")){
 8 
 9             window.attachEvent('onmessage',function(e){ self.handleSelectAddrMessage( e.data );});
10 
11         }
12 
13         else {
14 
15             window.addEventListener('message',function(e){ self.handleSelectAddrMessage( e.data );},false);
16 
17         }
18 
19       },
20 
21       //監聽選址信息
22 
23       handleSelectAddrMessage( addrMessageData ){
24 
25         let self = this ;
26 
27         //轉換為標准的JSON字符串
28 
29         let addrMsgObj = {};
30 
31         if( typeof addrMessageData === 'string' && addrMessageData.indexOf( 'c3Code' ) > 0 ){
32 
33           var reg = /({\S*?:)|(,\S*?:)/g
34 
35           var formatedJSONStr = addrMessageData.replace( reg , function( a , m1 , m2 ){
36 
37               if( m1 ){   //如果第一個分組匹配上
38 
39                 return '{"' + a.slice( 1 , -1 ) + '":';
40 
41               }
42 
43               else{
44 
45                 return ',"' + a.slice( 1 , -1 ) + '":';
46 
47               }  
48 
49           } );
50 
51           formatedJSONStr = formatedJSONStr.replace( /\'/g , '"' );
52 
53           let addrMsgObj = {};
54 
55           addrMsgObj = JSON.parse( formatedJSONStr );
56 
57           if( addrMsgObj.c3Code ){ 
58 
59             self.formReviewList[self.listIndex].resourceC3 = self.cityCodeMap[ addrMsgObj.c3Code ]  ;
60 
61             self.formReviewList[self.listIndex].resourceC4 = addrMsgObj.c4name  ;
62 
63             self.formReviewList[self.listIndex].resourceAddress = addrMsgObj.address ;
64 
65             self.formReviewList[self.listIndex].resourceAddressId = addrMsgObj.addressId ;
66 
67             self.formReviewList[self.listIndex].resourcePort = addrMsgObj.epon + ',' + addrMsgObj.gpon + ',' + addrMsgObj.exchName ;
68 
69             //關閉選址的對話框
70 
71             self.addrSelectDialogVisible = false ;
72 
73             self.listIndex = '' ;
74 
75           }
76 
77         }
78 
79       },
80 
81  
82 
83 created(){
84 
85   let self = this;
86 
87   self.getWorkOrderList();
88 
89   //監聽選址的事件響應
90 
91   self.registerSelectAddrMessage();
92 
93 },

 

 

為了每次進入時都是新的b頁面,做個重載,第一種方式沒行通,用了第二種。

重載:

if( self.$refs.iframeId ){

  // 第一種重載必須同域

  // self.$refs.iframeId.contentWindow.location.reload( true );

  // 第二種每次賦值路徑,可同域可跨域

  self.$refs.iframeId.src = 'http://134.96.249.135:8088/ass/webLogin.do?areaCode=571'

};

 

效果:

 

 

 

 

所以在代碼里做了監聽** ★,°:.☆( ̄▽ ̄)/$:.°★ **。

 

再舉個小栗子

father.html通過iframe包含了son.html

father.html

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset="utf-8" />
 5          <script type="text/javascript">
 6         function say(){
 7             alert("這是father的say()");
 8         }
 9         function callChild(){
10             myFrame.window.say();
11             myFrame.window.document.getElementById("button").value="調用結束"
12         }
13     </script>
14     </head>
15     <body>
16          <input id="button" type="button" value="調用son.html中的函數say()" onclick="callChild()"/>
17     <iframe name="myFrame" src="son.html"></iframe>
18     </body>
19 </html>

 

son.html

 1 <html>
 2     <head>
 3         <meta charset="utf-8" />
 4         <script type="text/javascript">
 5             function say() {
 6                 alert("這是son的say()");
 7             }
 8             function callParent() {
 9                 parent.say();
10                 parent.window.document.getElementById("button").value = "調用結束";
11             }
12         </script>
13     </head>
14     <body>
15         <input id="button" type="button" value="調用father.html中的say()函數" onclick="callParent()" />
16     </body>
17 </html>

方法是如何調用的?獲取子頁面或父頁面的window對象,在通過對象調用方法。

父頁面調用子頁面方法:FrameName.window.childMethod();
子頁面調用父頁面方法:parent.window.parentMethod();
 
vue版的

 

 

 vue文件里的寫法:

 

 

iframe文件里的js的寫法:

 

 


在方法調用前,以下點必須要注意!!!
要確保在iframe加載完成后再進行操作,如果iframe還未加載完成就開始調用里面的方法或變量,會產生錯誤。判斷iframe是否加載完成有兩種方法:
1. iframe上用onload事件
2. 用document.readyState=="complete"來判斷


免責聲明!

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



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