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對象,在通過對象調用方法。

vue文件里的寫法:

在方法調用前,以下點必須要注意!!!