记一次与iframe之间的抗争
iframe這個(gè)標(biāo)簽之前了解過(guò)這個(gè)東西,知道它可以引入外來(lái)的網(wǎng)頁(yè),但是實(shí)際開發(fā)中沒(méi)有用到過(guò)。這一次有一個(gè)需求是說(shuō)準(zhǔn)備要在網(wǎng)頁(yè)中嵌套另外一個(gè)網(wǎng)站,用iframe這個(gè)標(biāo)簽,讓我測(cè)試一下這個(gè)可不可以在自己的網(wǎng)頁(yè)中對(duì)引入進(jìn)來(lái)的iframe框架進(jìn)行操作,操作dom和css的一些東西。讓我做出一個(gè)小案例看看可不可以,我信誓旦旦保證說(shuō)可以的,我試過(guò)!!!
就這樣交代給我之后信心滿滿的就開始了我的驗(yàn)證。
什么是同源?
同域名、 同端口、 同協(xié)議??
網(wǎng)上是有好多這個(gè)的解釋的,給出一張圖片。 看下面這張圖片。 引用來(lái)自? 瀏覽器的同源策略
?
我直接新建了一個(gè)文件夾,在里面寫了兩個(gè)html頁(yè)面的文件,舉例說(shuō)明是a.html和b.html,然后讓其中的一個(gè)a.html文件中用iframe標(biāo)簽的src去引入b.html文件,在里面去互相操作他們的css樣式和DOM元素。
a.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><style>html,body{height: 100%;}body{background: pink;}#iframe1{width: 400px;height: 400px;margin: auto;background: blue;}</style> </head> <body>這里是父文檔 <input type="button" id="btn1" value="改變子文檔的顏色"> <input type="button" id="btn2" value="刪除span1"> <input type="button" id="btn3" value="改變span2的顏色"><input type="button" id="btn4" value="修改子文檔中的link標(biāo)簽"> <br /> <br /> <hr /> <iframe id="iframe1" src="b.html" frameborder="0"></iframe><script>// 只有同服務(wù)器下 同域名下才可以操作 不能更改別人的網(wǎng)頁(yè)。。var oBtn1 = document.getElementById('btn1');var oIframe1 = document.getElementById('iframe1');function fn(){document.body.style.background = 'green';}oBtn1.onclick = function () {console.log(oIframe1.contentWindow); // ---這個(gè)東西是子文檔中的window對(duì)象console.log(oIframe1.contentDocument); // ---- 這個(gè)東西是子文檔中的document對(duì)象oIframe1.contentWindow.document.body.style.background = 'yellow';};btn2.onclick = function () {var span1 =oIframe1.contentWindow.document.querySelector('.span1');console.log(span1);span1.parentNode.removeChild(span1);};btn3.onclick = function () {var span2 =oIframe1.contentWindow.document.querySelector('.span2');span2.style.color = 'red';};btn4.onclick = function () {var iFrameWindow = oIframe1.contentWindow;console.log(iFrameWindow.document.getElementsByTagName('link'));var link0 = iFrameWindow.document.getElementsByTagName('link')[0];console.log(link0.parentNode.removeChild(link0));}</script></body> </html>?
b.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><style>body{background: yellowgreen;}</style><link rel="stylesheet" href="1.css"><link rel="stylesheet" href="2.css"> </head> <body><h1 id="h1">這里是子文檔1</h1><span class="span1">span1標(biāo)簽</span> <br><br><span class="span2">span2標(biāo)簽</span><span class="span3">span3標(biāo)簽</span><br /><hr /><input type="button" id="btn1" value="改變父文檔的顏色"><script>var oH1 = document.getElementById('h1');var oBtn1 = document.getElementById('btn1');oH1.onclick = function () {alert('子文檔中的點(diǎn)擊事件,我可以改變父文檔');console.log(window.parent); // -----這個(gè)parent對(duì)象是父文檔中的 window對(duì)象};oBtn1.onclick = function () {(function (window,document) {document.body.style.background = 'skyblue';})(window.parent, window.parent.document);};</script> </body> </html>樣式如下
?
上面的兩個(gè)代碼中用到了一個(gè)東西,在a.html文件中 用到了iframe標(biāo)簽元素的? .contentWindow 和 .contentDocument 這兩個(gè)東西,它們兩個(gè)分別是子文檔也就是b.html中的window對(duì)象和document對(duì)象,那么你說(shuō)知道了這兩個(gè)東西要去操作它里面的東西還不簡(jiǎn)單嗎。
在 b.html文件中的? window.parent 這個(gè)東西是a.html的window對(duì)象,那么它同樣也可以去操作a.html中的元素了。所以交給我的任務(wù)我感覺(jué)完成了,就去問(wèn)他,這樣可以。然后我給他看了一下這個(gè)東西,后來(lái)了解到這兩個(gè)不是同一個(gè)域名下的,這兩個(gè)網(wǎng)站不是在一起的,然后我就回來(lái)又來(lái)調(diào)試。
?
不同端口下的調(diào)試
我經(jīng)常用的編輯器是webstrom,它這個(gè)東西會(huì)自啟動(dòng)一個(gè) 127.0.0.1:63342的端口,我又用node做了一個(gè)簡(jiǎn)單的監(jiān)聽 3000端口的服務(wù)器,在網(wǎng)頁(yè)上面打開了。
還是同樣的代碼吧,只不過(guò)把ifreme上面的src改為了我3000端口的網(wǎng)頁(yè)。
但是這次瀏覽器給了我一個(gè)驚喜,因?yàn)槲腋杏X(jué)吧只有后端才會(huì)存在跨域什么的問(wèn)題,沒(méi)有想過(guò)前端的這些東西。
它的打印出來(lái)的window對(duì)象都變了,好多都是false了,和之前在同一個(gè)頁(yè)面下面的東西都不一樣了~~
因?yàn)橐豢吹?origin? cross-origin就感覺(jué)是跨域的那種問(wèn)題。
得了吧,去百度,google查到底怎么辦吧。我一直相信以我現(xiàn)在的水平遇到的問(wèn)題其他的人同樣也有人會(huì)遇到過(guò)。
這一查不要緊,感覺(jué)看得好多文章開辟除了新的天地,真的是,在文章底部會(huì)給出參考文章,昨天我只看到了一種解決方案,并且將它付諸于實(shí)踐了,但是由于想要搞明白今天又找到了幾種解決方案,但是并沒(méi)有去試驗(yàn)。
還是要講一講同源對(duì)哪些行為有限制?
隨著互聯(lián)網(wǎng)的發(fā)展,同源策略 越來(lái)越嚴(yán)格。目前,如果非同源,共有三種行為受到限制。
1. Cookie、localStorage和 IndexDB無(wú)法讀取
2. DOM無(wú)法獲得
3. AJAX 請(qǐng)求不能發(fā)送
雖然這些限制是必要的,但是有時(shí)很不方便,合理的用途也會(huì)受到影響。
這個(gè)問(wèn)題難道就沒(méi)有辦法解決了嗎?有的
Cookie解決方法
Cookie 是服務(wù)器寫入瀏覽器的一小段信息,只有同源的網(wǎng)頁(yè)才能共享。但是,兩個(gè)網(wǎng)頁(yè)一級(jí)域名相同,只是二級(jí)域名不同,瀏覽器允許通過(guò)設(shè)置document.domain共享 Cookie。
舉例來(lái)說(shuō),A網(wǎng)頁(yè)是http://w1.example.com/a.html,B網(wǎng)頁(yè)是http://w2.example.com/b.html,那么只要設(shè)置相同的document.domain,兩個(gè)網(wǎng)頁(yè)就可以共享Cookie。
document.domain = 'example.com';
現(xiàn)在,A網(wǎng)頁(yè)通過(guò)腳本設(shè)置一個(gè) Cookie。
document.cookie = "test1=hello";
B網(wǎng)頁(yè)就可以讀到這個(gè) Cookie。
var allCookie = document.cookie;
注意,這種方法只適用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 無(wú)法通過(guò)這種方法,規(guī)避同源政策,而要使用下文介紹的PostMessage API。
另外,服務(wù)器也可以在設(shè)置Cookie的時(shí)候,指定Cookie的所屬域名為一級(jí)域名,比如.example.com。
Set-Cookie: key=value; domain=.example.com; path=/
這樣的話,二級(jí)域名和三級(jí)域名不用做任何設(shè)置,都可以讀取這個(gè)Cookie。
上面這種方法暫時(shí)還沒(méi)有去試驗(yàn),等試驗(yàn)過(guò)后再來(lái)修改一下這里,因?yàn)樽约憾疾恢佬胁恍小?/p>
?
iframe
如果兩個(gè)網(wǎng)頁(yè)不同源,就無(wú)法拿到對(duì)方的DOM,上面的第二個(gè)例子我已經(jīng)去試驗(yàn)過(guò)了,也看到報(bào)錯(cuò)信息了。
就是父窗口運(yùn)行下面的命令,如果iframe窗口不是同源,就會(huì)報(bào)錯(cuò)。
document.getElementById("myIFrame").contentWindow.document // Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.上面命令中,父窗口想獲取子窗口的DOM,因?yàn)榭缬蛸Y源導(dǎo)致報(bào)錯(cuò)。
反之亦然,子窗口獲取主窗口的DOM也會(huì)報(bào)錯(cuò)。
window.parent.document.body // 報(bào)錯(cuò)前面講的這些實(shí)際上我第二個(gè)例子試驗(yàn)過(guò)了,下面也就是我遇到問(wèn)題的幾種解決方法。
參考的他人的文章找到的,對(duì)于完全不同源的網(wǎng)站,目前有三種方法,來(lái)解決我遇到的問(wèn)題。
?
1. 片段標(biāo)識(shí)符
片段標(biāo)識(shí)符呢也就是哈希值#,我們都知道當(dāng)網(wǎng)址資源#前面不變,后面的部分變化的時(shí)候網(wǎng)頁(yè)是不會(huì)刷新的,如果不清楚的話,可以看一下我之前寫過(guò)的一篇文章? 淺談SPA? ,里面有詳細(xì)的介紹#。
就是父文檔和子文檔之間要交互時(shí),就去改變hash值 也就是#后面的部分,然后兩者再相互去監(jiān)聽hash變化的事件,再去自己做一些處理就好了
window.location.hash; // 這個(gè)是可以獲取hash值的window.onhashchange = function(){// 這個(gè)是hash值改變會(huì)觸發(fā)這個(gè)函數(shù) }
舉一個(gè)小例子可以去試驗(yàn)一下
父窗口可以把信息,寫入到子窗口的片段標(biāo)識(shí)符
父窗口中的代碼
為了好看吧,就不用博客園自帶的那個(gè)代碼了,就這樣把子文檔的url地址給改變了吧,因?yàn)?不會(huì)刷新網(wǎng)頁(yè),而子文檔中也可以監(jiān)聽到這個(gè)#值的改變,所以子文檔中
瀏覽器中打印的東西
在這里可以看到了,我們傳遞過(guò)去的數(shù)據(jù)信息為 #changeColor,在子頁(yè)面中可以判斷#后面的帶的東西,再去執(zhí)行自己的邏輯。
?
同樣的子文檔給父文檔傳遞數(shù)據(jù)
我們不是可以拿到父文檔的那個(gè) window.parent嗎,就用這個(gè)去改變就可以了,但是? BUT!!!
我在子頁(yè)面中使用的時(shí)候
btn1.onclick = function () {console.log(parent.location); }
?這是什么嘛,你父文檔都可以改子文檔了,問(wèn)什么這個(gè)還是要 block frame with啥啥啥的,我本來(lái)以為可以成功的,這個(gè)有知道解決方法的大佬可以幫幫忙嗎。嘿嘿,暫時(shí)先這樣吧,父文檔已經(jīng)可以給子文檔傳遞信息了,我的解決方法也不是這種,暫時(shí)先把這個(gè)錯(cuò)誤問(wèn)題放一放,以后有解決方法了,會(huì)來(lái)這里修改的。
?
2. window.name
瀏覽器窗口有window.name屬性,這個(gè)屬性最大的特點(diǎn)是,無(wú)論是否同源,只要在同一個(gè)窗口里面,前一個(gè)窗口設(shè)置了這個(gè)屬性后,后一個(gè)網(wǎng)頁(yè)可以讀取它。
父窗口先打開一次子窗口,載入一個(gè)不同源的網(wǎng)頁(yè),將網(wǎng)頁(yè)信息寫入window.name屬性。
window.name = data;
接著,子窗口跳回一個(gè)與主窗口同域的網(wǎng)址
location = 'http://parent.url.com/xxx.html'然后主窗口就可以讀取子窗口的window.name了
var data = document.getElementById('myFrame').contentWindow.name;
這種方法的優(yōu)點(diǎn)是,window.name容量很大,可以放置非常長(zhǎng)的字符串;缺點(diǎn)是必須監(jiān)聽子窗口window.name屬性的變化,影響網(wǎng)頁(yè)性能。
3.跨文檔通信API? postMessage
? 我用到的解決方法是這種方法,感覺(jué)它和Vue之間的組件傳值一樣,不說(shuō)話了,直接上代碼,測(cè)試吧,記得那個(gè)子文檔是 3000端口的頁(yè)面
父文檔
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><style>body{background: pink;}#iframe1{width: 100%;height: 400px;}</style> </head> <body><input type="button" id="btn1" value="改變子文檔的東西"> <input type="button" id="btn2" value="刪除span1的顏色"> <input type="button" id="btn3" value="改變span2的顏色"> <br /> <hr /> <!--<iframe id="iframe1" src="http://localhost:3000" frameborder="0"></iframe>--> <iframe id="iframe1" src="http://localhost:3000" frameborder="0"></iframe> <script>var oIframe1 = document.getElementById('iframe1');var a = function fn(){document.body.style.background = 'green';};btn1.onclick = function () {console.log('傳遞的數(shù)據(jù)是','messageInfo');oIframe1.contentWindow.postMessage('changeColor',"http://localhost:3000");};btn2.onclick = function () {//oIframe1.contentWindow.postMessage('changeSpan2Color',"http://localhost:3000");oIframe1.contentWindow.postMessage('deleteSpan1',"http://localhost:3000")};btn3.onclick = function () {oIframe1.contentWindow.postMessage('changeSPan2Color',"http://localhost:3000")};window.addEventListener('message',function (res) {console.log(`這里是父文檔`);if(res.data == 'GetWhiteLabel')document.body.style.background = 'yellow';})</script> </body> </html>子文檔
<!doctype html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>body{background: skyblue;}</style> </head> <body><h1>這里是我的html頁(yè)面呢</h1><span class="span1">span1標(biāo)簽</span><span class="span2">span2標(biāo)簽</span> <br><input type="button" id="btn1" value="改變父文檔的東西"><script><!---->window.onload = function () {let parent = window.parent;window.addEventListener('message',function (res) {console.log(`******************這里是子頁(yè)面的接收到的消息*************`);console.log(res);switch (res.data) {case 'changeColor':document.body.style.background = 'green';break;case 'deleteSpan1':var oSpan1 = document.querySelector('.span1');oSpan1.parentNode.removeChild(oSpan1);break;case 'changeSPan2Color':var oSpan2 = document.querySelector('.span2');oSpan2.style.color = 'red';break;}});btn1.onclick = function () {parent.postMessage("GetWhiteLabel","*");}}</script> </body> </html>還是同樣的頁(yè)面吧,實(shí)現(xiàn)一樣的功能。
?
?上面有的地方寫的不太好,存在一些安全問(wèn)題,這個(gè)正是我現(xiàn)在正在做的地方,
oframe.contentWindow.postMessage(data,origin,false); //這個(gè)是postMessage的API // 發(fā)送的數(shù)據(jù) 子文檔地址 false// 在子文檔中去監(jiān)聽那個(gè)message的變化 window.addEventListener("message",function(res){console.log(res.data); //這個(gè)東西就是發(fā)送過(guò)去的數(shù)據(jù)// 去根據(jù)傳過(guò)來(lái)的不同的數(shù)據(jù) 再去做相應(yīng)的判斷 })子文檔給父文檔傳數(shù)據(jù)的方式
parent.postMessage('message',origin,false); // 同樣也是類似的// 在父文檔中去監(jiān)聽那個(gè)message的值window.addEventListener("message",function(res){console.log(res.data); //這個(gè)東西就是發(fā)送過(guò)去的數(shù)據(jù)// 去根據(jù)傳過(guò)來(lái)的不同的數(shù)據(jù) 再去做相應(yīng)的判斷});?這樣做可以實(shí)現(xiàn),就是有一些安全問(wèn)題,就是所有人如果查看你網(wǎng)站的源碼的話肯定會(huì)看到這個(gè)東西的,你寫的這么隨意,其他任何網(wǎng)站只要引入你的子文檔,然后就可以通過(guò)自己去寫一些東西去改變你的子文檔。
另外的一個(gè)缺點(diǎn)就感覺(jué)是 拓展性不好,你還需要去拿到子文檔的網(wǎng)站,還需要再去修改它的源代碼,感覺(jué)特別麻煩,如果有好幾十個(gè)頁(yè)面還要寫好幾十個(gè)嗎。
所以想到了一種傳值的方法,不穿那個(gè)要判斷的東西,把要修改的元素的html代碼的函數(shù)給傳過(guò)去,就是子文檔去定義一個(gè)接口去接收,父文檔把要執(zhí)行的事件都傳過(guò)去,然后子文檔寫一個(gè)執(zhí)行事件的接口。這里就會(huì)出現(xiàn)剛才第一個(gè)那個(gè)安全問(wèn)題了,更為嚴(yán)重,因?yàn)槟悴恢酪獔?zhí)行的是什么事件。
?
現(xiàn)在有一個(gè)想法就是后臺(tái)做一個(gè)認(rèn)證,就像微信的那個(gè)access_token認(rèn)證一樣的東西,加密,每次去操作子文檔的時(shí)候,都要去請(qǐng)求,然后在子頁(yè)面監(jiān)聽到這個(gè)事件之后解密再兩個(gè)之間去對(duì)比,如果一樣了才去執(zhí)行,這個(gè)暫時(shí)還不知道如何去下手。
?
? 本文感覺(jué)特別有用的解決辦法和參考的文章有如下還有更多的沒(méi)有發(fā),如果你遇到了同樣的問(wèn)題,看了我的解決不了問(wèn)題,可以去看看下面的文章,當(dāng)然還可以給我留言,必回復(fù)。
stackoverflow網(wǎng)頁(yè)中的問(wèn)答? ? ?博客園園友的文章 關(guān)于iframe的實(shí)踐? ? ?阮一峰大佬的? 瀏覽器同源政策及其規(guī)避方法
如果有更好的解決辦法還望可以告知,謝謝!
如果你閱讀了本文章有了一些收獲,我會(huì)感到非常開心,由于能力有限,文章有的部分解釋的不到位,希望在以后的日子里能慢慢提高自己能力,如果不足之處,還望指正。
轉(zhuǎn)載于:https://www.cnblogs.com/z937741304/p/9728160.html
總結(jié)
以上是生活随笔為你收集整理的记一次与iframe之间的抗争的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Nginx+tomcat集群的sessi
- 下一篇: 网络流24题之餐巾计划问题