iframe -- postMessage

之前一提到跨域,都是前端到后臺的問題.

其實(shí),在網(wǎng)頁中嵌套非同源的iframe也存在跨域的問題.

比如,在你自己的頁面里利用 iframe 嵌套百度的網(wǎng)頁,兩個(gè)頁面存在通信的話,就存在跨域的問題.

對.如果不存在通信,就不存在所謂的跨域問題.


iframe 是干嘛的?

在當(dāng)前網(wǎng)頁中利用 iframe 可以嵌入另一個(gè)完整的網(wǎng)頁.

這個(gè)就是 iframe 干的事情.

這樣做的意義在哪呢?為什么我要在我的一個(gè)網(wǎng)頁里嵌入另外一個(gè)網(wǎng)頁?

  • 有時(shí)候是廣告.(別人開發(fā)的網(wǎng)頁,放在我們網(wǎng)頁指定的位置)
  • 有時(shí)候是業(yè)務(wù)邏輯太復(fù)雜,需要單獨(dú)的一個(gè)網(wǎng)頁去承載.于是把這個(gè)比較復(fù)雜的邏輯網(wǎng)頁就放到網(wǎng)頁中了.

其實(shí)都是我瞎謅的.為了讓自己能夠更快的理解這個(gè)玩意,編就編吧.


iframe 嵌入自己的網(wǎng)頁.

開發(fā)web的時(shí)候,使用iframe嵌入自己的網(wǎng)頁.

這里嵌入自己的網(wǎng)頁潛臺詞就是兩個(gè)網(wǎng)頁是同源的.

我們自己開發(fā)的網(wǎng)頁,當(dāng)然是部署在自己的服務(wù)器上.

不出意外的話,那協(xié)議+域名+端口號都是一致的.

所以,它們是同源.

iframe 的基本語法如下.

<iframe src="2.html" height="300" width="500" id='demo' name="demo"></iframe>

項(xiàng)目結(jié)構(gòu):

image.png

同源 3 個(gè)靜態(tài)頁面. 1.html 2.html 3.html

  • 在 1.html 中嵌入 2.html
  • 在 2.html 中嵌入 3.html
image.png

1.html

<body>
  <h1 id="h1">我是1.html網(wǎng)頁</h1>
  <iframe src="2.html" height="300" width="500" id='demo' name="demo"></iframe>
</body>

2.html

<body>
  <h1 id="h1">我是2.html網(wǎng)頁</h1>
  <iframe src="3.html" width="200" height="200" id="demo2" name="demo2"></iframe>
</body>

3.html

<body>
  <h1 id="h1">我是3.html</h1>
</body>

嵌入完畢之后呢?

光顯示的話,也就到這結(jié)束了.

如果需要操作同源下嵌套的iframe.
可以按照以下步驟.

1.獲取指定的iframe

1.html
<!--同源嵌套2.html-->
<iframe src="2.html" height="300" width="500" id='demo'></iframe>

let iframe = document.getElementById('demo')

接跟獲取一個(gè)dom元素一樣.利用 getElementById('demo') 即可.

2.獲取iframe內(nèi)部一些關(guān)鍵屬性

對于一個(gè)完整的html頁面來說.
它有window,也有document.

對于嵌套的iframe來說,也不例外.

但是指的注意的是,一定要在等待iframe這個(gè)嵌套頁面加載完畢之后,在去進(jìn)行獲取.

iframe頁面是異步加載的.

// 等待iframe嵌套的頁面加載完畢
iframe.onload = function () {
    let iframeWindow = iframe.contentWindow
    let iframeDocument = iframe.contentDocument
}

3. 在同源的情況下

iframe 可以等同于一個(gè)普通的DOM節(jié)點(diǎn)(不過它是異步加載的)
拿到這個(gè)iframe的document和window之后

iframe.onload = function () {
    let iframeWindow = iframe.contentWindow
    let iframeDocument = iframe.contentDocument
    
    iframeDocument.getXXXX ==== 獲取或修改嵌套iframe的dom結(jié)構(gòu).就像操作自己的document一樣
    iframeWindow.xxxx === 獲取嵌套iframe的方法或者屬性或者對象.就像操作自己的window一樣.
}

幾個(gè)注意點(diǎn):

  • iframe 嵌套獲取只能獲取一層.(比如1.html 嵌套 iframe(2.html) -> 2.html 嵌套 iframe(3.html).那么 1.html只能獲取到2.html 2.html只能獲取到3.html

  • window.top 獲取當(dāng)前iframe嵌套層級的最頂級(這里是1.html)

  • window.parent 獲取當(dāng)前iframe的上一層級(2.html就是1.html,3.html就是2.html)

image.png

同源iframe嵌套總結(jié):

  • 可以把嵌套的iframe理解成一個(gè)普通的大DOM節(jié)點(diǎn).
  • 它是異步加載的必須等待onload執(zhí)行完成.
  • 指向完成后拿到 contentWindow 和 contentDocument 之后,就可以無縫的操作了.
  • 注意,iframe只能拿一層.

跨域的iframe通信

兩個(gè)頁面

  • 一個(gè) 1.html 鏈接地址為: http://127.0.0.1:12345/1.html
  • 一個(gè) 4.html 鏈接地址為: http://127.0.0.1:50874/iframe-01/4.html
1.html -> http://127.0.0.1:12345/1.html
<body>
   <iframe src="http://127.0.0.1:50874/iframe-01/4.html" frameborder="0" id='frame'></iframe>
</body>
4.html ---> http://127.0.0.1:50874/iframe-01/4.html
<body>
    <h1>我是4.html 端口號50874</h1>
</body>
<script>
    // 在 4.html定義的全局對象 window.obj
    var global4Obj = {
    name: '李四'
  }
</script>

它倆的端口號不一致.

12345 | 50874

端口號不一致時(shí),并不影響iframe嵌套.
(經(jīng)常瞎搞在自己頁面里嵌套一個(gè)baidu首頁)

但是會影響它倆之前的數(shù)據(jù)跨域請求.

比如,按照同源的方式,去操作iframe的頁面,會得到這樣一個(gè)提示.

1.html -> http://127.0.0.1:12345/1.html
let iframe = document.getElementById('frame')
  iframe.onload = function () {
    const window = iframe.contentWindow
    const document = iframe.contentDocument
    console.log('window',window)
    console.log('document',document)
  }

1.html控制臺輸出

image.png

發(fā)現(xiàn) document 獲取不到.但是獲取的到window.

window上有設(shè)置了一個(gè)全局對象 obj

于是輸出:

  let iframe = document.getElementById('frame')
  iframe.onload = function () {
    const window = iframe.contentWindow
    const document = iframe.contentDocument
    console.log('window',window)
    console.log('document',document)
    console.log(window.obj.name)
  }
image.png

很明顯的錯(cuò)誤提示,操作跨域了.
不能訪問跨域iframe的window上的全局屬性和方法.
拿不到document(這里為null)了,就更加不能操作dom元素了.

所以,如果嵌套的iframe跨域了,默認(rèn)情況下只能加載下來看,不能做任何其他的操作.


利用postMessage進(jìn)行iframe跨域通信

方式一:同一級域名不同二級域名

比如 www.a.com/index.htmlapi.a.com/index.html

由于它們的的一級域名一一致.

可以利用 document.domain 進(jìn)行跨域操作.

a.html
document.domain = 'a.com'
b.html
document.domain = 'b.com'

雙方都設(shè)置同樣的域之后,就可以像同源非跨域的iframe那樣操作了.

方式二.使用postMessage

看了很多博客關(guān)于postMessage方法的使用.

大致說的都是:

如果嵌套的iframe存在跨域,那么就可以使用postMessage進(jìn)行通信.

于是心想,這也太簡單了吧.

就開始吭哧吭哧寫代碼.

image.png
1.html -> http://127.0.0.1:12345/1.html
<body>
  <!-- <b>12345</b> -->
  <h1>我是1.html</h1>
  <p></p>
  <iframe src="http://127.0.0.1:50874/iframe-01/4.html" frameborder="0" id='frame'></iframe>
</body>

4.html ->http://127.0.0.1:50874/iframe-01/4.html

<body>
  <h2>我是4.html</h2>
  <p></p>
</body>

現(xiàn)在,我想讓 1.html 跨域的給 2.html 傳遞數(shù)據(jù).

于是在 1.html 中.

window.postMessage('1.html的數(shù)據(jù)','http://127.0.0.1:50874/')

在 4.html 中

  window.addEventListener('message', function (e) {
    console.log(e.data)
  },false)

想著非常完美,也太簡單了.

執(zhí)行瀏覽器.

image.png

發(fā)現(xiàn)報(bào)錯(cuò)了.

回想一下:

  • 1.html
  • 1.html 中利用 iframe 嵌套了 2.html
  • 它倆的屬于不同的域(端口號不同)
  • 現(xiàn)在我想從 1.html 傳遞數(shù)據(jù)到 2.html.
  • 1.html 里面使用 window.postMessage() 發(fā)現(xiàn)報(bào)錯(cuò)了.

問題出在哪?

重新查看API文檔之后,發(fā)現(xiàn)理解是錯(cuò)的.

本質(zhì)上利用 postMessage 跨域,不是 1.html2.html 發(fā)數(shù)據(jù).

應(yīng)該是是 2.html2.html 發(fā)數(shù)據(jù)

體現(xiàn)在代碼上應(yīng)該是就是:

1.html -> http://127.0.0.1:12345/1.html
window.onload = function () {
    let frame = document.getElementById('frame')
    // 相當(dāng)于還是自己在給自己傳啊!!!
    document.getElementById('text').addEventListener('input', function () {
      frame.contentWindow.postMessage(this.value, 'http://127.0.0.1:50874/')
    }, false)
  }

傳遞數(shù)據(jù)的是 frame.contentWindow.postMessage
而不是想當(dāng)然的 window.postMessage

測試一下想法.

1.html中 --> http://127.0.0.1:12345/1.html

1.html -> http://127.0.0.1:12345/1.html

<body>
  <!-- <b>12345</b> -->
  <h1>我是1.html</h1>
  <p></p>
  <iframe src="http://127.0.0.1:56434/iframe-01/4.html" width="500" height="300" id='frame'></iframe>
  <!-- 設(shè)置一個(gè)按鈕,點(diǎn)擊按鈕往跨域的4.html發(fā)送數(shù)據(jù) -->
  <button class="postMessage">postMessage</button>
  <!-- 用于接受4.html跨域提交過來的數(shù)據(jù) -->
  <p class="result"></p>
</body>

1.html -> http://127.0.0.1:12345/1.html
<script>
// 第一步,要拿到iframe,這里主要是拿到iframe.contentWindow
  let iframe = document.getElementById('frame')
  let iframeWindow = null
  iframe.addEventListener('load', function () {
    console.log('iframe loaded')
    iframeWindow = iframe.contentWindow // 利用iframe.contentWindow 也就是4.html 的window對象.
  },false)
  
// 給4.html利用postMessage跨域發(fā)送數(shù)據(jù)
let postMessageButton = document.querySelector('.postMessage')
  postMessageButton.addEventListener('click', function () {
    // 這里是使用iframeWindow.也就是iframe自己的window發(fā)送數(shù)據(jù).
    // 而不是使用window.
    // 就相當(dāng)于使用 postMessage 其實(shí)本質(zhì)上還是自己在給自己發(fā)數(shù)據(jù).
    iframeWindow.postMessage('1.html發(fā)送過來的數(shù)據(jù)','http://127.0.0.1:56434') // 第二個(gè)參數(shù),指定4.html的域名
  }, false)
  
  // 用于接收 4.html提交回來的數(shù)據(jù)
  const result = document.querySelector('.result')
  window.addEventListener('message', function (event) {
    result.innerText = event.data
    console.log(event.source)
    console.log(event)
  }, false)
</script>

在4.html中 --> ->http://127.0.0.1:50874/iframe-01/4.html

4.html -> http://127.0.0.1:50874/iframe-01/4.html

<body>
  <h2>我是4.html</h2>
  <p></p>
  <!-- 用于接受 1.html 使用 postMessage 發(fā)送過來的數(shù)據(jù) -->
  <p class="result"></p>
  <!-- 設(shè)置一個(gè)按鈕給 1.html 發(fā)送數(shù)據(jù) -->
  <button class="postMessage">postMessage</button>
</body>

4.html->http://127.0.0.1:50874/iframe-01/4.html

<script>
   let result = document.querySelector('.result')
  window.addEventListener('message', function (e) {
    result.innerText = e.data
  },false)

  let postMessageButton = document.querySelector('.postMessage')
  postMessageButton.addEventListener('click', function () {
    window.parent.postMessage('4.html發(fā)送過來的數(shù)據(jù)','http://127.0.0.1:12345/') // 注意,這里由于我們知道 4.html 被 1.html 嵌套了,所以使用 window.parent 可以拿到 1.html 環(huán)境中的window.
    
    // 由于我們也知道 1.html 是當(dāng)前iframe嵌套層級中的最頂層,也可以使用window.top.postMessage() ....
  }, false)   
</script>

查看結(jié)果:

image.png

最后總結(jié):

  • 跨域不光是前臺都后臺的跨域.
  • iframe嵌套不同源的資源也存在跨域.
    • 如果一級域名相同,可以使用document.domain 進(jìn)行跨域操作.
    • 如果域名完全不同,可以使用 postMessage 進(jìn)行跨域.
      • postMessage 傳遞數(shù)據(jù),本質(zhì)上仍然是自己給自己傳
      • 在給某個(gè)iframe傳遞數(shù)據(jù)時(shí),必須首先拿到當(dāng)前iframe的contentWindow.然后在contentWindow上使用postMessage 傳遞數(shù)據(jù).

碼云code

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尼桶,一起剝皮案震驚了整個(gè)濱河市昏兆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浑吟,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡软棺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門尤勋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喘落,“玉大人,你說我怎么就攤上這事最冰∈萜澹” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵暖哨,是天一觀的道長赌朋。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么沛慢? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任服球,我火速辦了婚禮,結(jié)果婚禮上颠焦,老公的妹妹穿的比我還像新娘斩熊。我一直安慰自己,他們只是感情好伐庭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布粉渠。 她就那樣靜靜地躺著,像睡著了一般圾另。 火紅的嫁衣襯著肌膚如雪霸株。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天集乔,我揣著相機(jī)與錄音去件,去河邊找鬼。 笑死扰路,一個(gè)胖子當(dāng)著我的面吹牛尤溜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汗唱,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼宫莱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哩罪?” 一聲冷哼從身側(cè)響起授霸,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎际插,沒想到半個(gè)月后碘耳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡框弛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年辛辨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片功咒。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愉阎,死狀恐怖绞蹦,靈堂內(nèi)的尸體忽然破棺而出力奋,到底是詐尸還是另有隱情,我是刑警寧澤幽七,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布景殷,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏猿挚。R本人自食惡果不足惜咐旧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绩蜻。 院中可真熱鬧铣墨,春花似錦、人聲如沸办绝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孕蝉。三九已至屡律,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間降淮,已是汗流浹背超埋。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佳鳖,地道東北人霍殴。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像系吩,于是被迫代替她去往敵國和親繁成。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 元素會創(chuàng)建包含另外一個(gè)文檔的內(nèi)聯(lián)框架(即行內(nèi)框架)淑玫; 一巾腕、align 屬性(不贊成) align屬性規(guī)定ifram...
    puxiaotaoc閱讀 19,719評論 0 14
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)絮蒿。 注意:講述HT...
    kismetajun閱讀 27,453評論 1 45
  • 什么是跨域尊搬? 2.) 資源嵌入:、土涝、佛寿、等dom標(biāo)簽,還有樣式中background:url()但壮、@font-fac...
    電影里的夢i閱讀 2,368評論 0 5
  • 原文地址:原文地址 什么是跨域冀泻? 跨域是指一個(gè)域下的文檔或腳本試圖去請求另一個(gè)域下的資源,這里跨域是廣義的蜡饵。 廣義...
    C_Y大漁閱讀 1,258評論 1 13
  • 不管有千萬人阻擋弹渔,只怕自己投降。 愛情溯祸,如死之堅(jiān)強(qiáng)肢专。 愿有情人終成眷屬舞肆。
    花園小白閱讀 418評論 0 0