postMessage:主頁、iframe 頁可互相傳值

/

前端的一個困擾:管不到 iframe

前端有一件麻煩事就是 iframe 這件事,比方:頁面裡有個 iframe,主頁要抓 iframe 裡的東西,或是 iframe 想傳值給主頁。

如果主頁跟 iframe 頁同網域,JS 還可以由主頁抓到 iframe 頁,但兩頁不同網域的話就不行。

August 以前有遇過一個需求,有 2 個頁面,都是公司的,但網域不同,在 A 頁面要 iframe B 頁面,還要無違合的放進頁面裡,高度要剛好,不能出現 Scroll Bar,也不能被截掉。

但在不同網域下,是無法去抓 iframe 的值的,不論是高度或是裡面的參數什麼的。

這時,就需要 postMessage 這個好物,它可以讓 2 個不同網域的頁面互相傳值,因此就只要 B 頁面讀取完時,傳 B 頁面本身的高度給 A 頁,讓 A 頁改變 iframe 的 height 就行。

Google、Facebook 也是這樣傳值的

監聽一下 postMessage 事件並 console.log,會看見 Google、Facebook 也是這樣在傳值。

Google 廣告或是 FB 的讚按鈕,它們 iframe 到其它網站時,網域一定跟 Google、FB 的不同,而 iframe 又無法抓到外面頁面的值,或是外面頁面想傳值給 iframe 頁時怎麼辦?這時就要用 postMessage。

本篇筆記要記錄的就是 A、B 頁面 iframe 的情況下,A 傳給 B,或 B 傳給 A 的問題,該怎麼用 postMessage 來解決。


postMessage 基本使用

postMessage 是傳遞參數的功能,分為 2 部份:傳值、接值。

這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝

傳值,就是 A 把參數傳給 B,這是 postMessage。

接值,就是當 B 收到 A 傳來的 message 後要做什麼,這是 addEventListener message。


傳值

傳值就是用 postMessage,code 如下:

目標視窗.postMessage(要傳的參數, 目標的網域);

要傳的參數:就是要傳的參數(有夠直白XD),比方想傳高度,那就是寫高度。也可以寫一個陣列裡面包多個值。

目標的網域:postMessage 可以指定要傳給哪一個網域,在接值時也可以指定專收哪個網域來的 message。本篇主頁用 https://mother.tw,iframe 頁用 https://iframe.tw

目標視窗:有三種情況,要寫的值不一樣,如下

主頁傳值給 iframe

目標視窗就是 iframe:

var iframe = document.getElementById('iframe').contentWindow;
iframe.postMessage('message', 'https://iframe.tw');

iframe 傳值給主頁

目標視窗就是 parent:

parent.postMessage('message', 'https://mother.tw');

主頁用 window.open 的方式開了一個視窗

這部份 August 還沒遇過實際的需求,放上 MDN 的範例:

這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝

var popup = window.open(...popup details...);
popup.postMessage("hello there!", "http://example.org");

接值

監聽 message 事件就行了:

// 接到 message 要做什麼事的 function
function receiveMessage(e) {
  // 來源網址(e.origin)不是指定的網域時
  if(e.origin !== 'https://xxxxxxx.tw') {
    alert('資料來源錯誤');
    return false;
  }
  // 來源網址是指定的網域時
  else {
    // 拿傳來的參數(e.data)
    const data = e.data;
  }
}
 
 // 監聽 message 事件
 window.addEventListener('message', receiveMessage, false);

在傳值那段的 目標的網域 有寫,傳值、接值都可以指定網域,MDN 上是寫可以用萬用「*」代表不限網域,但那就是連外星人都可以傳值到頁面上,所以建議是寫上指定網域。

function 裡面用 e.origin 來確認傳訊息來的網域是不是你要接收的網域。

比方 iframe 要接來自 https://mother.tw 的 message,但實際頁面上可能還有 Google 廣告、FB 讚鈕的 message 在那傳個滿天飛,總不能每次有 message 來就不斷在執行 function,因此才要先寫 if(e.origin !== 'https://xxxxxxx.tw'){} else { // 做某件事 } 來確保來源網域是 mother.tw。


原始碼、Demo

August 寫了一個頁面 Demo,裡面有 2 個範例:

  1. iframe 頁傳訊息給母頁:iframe 的高度就是 iframe 頁面本身的高度,無縫 iframe
  2. 母頁傳訊息給 iframe 頁:母頁(mother.tw)傳訊息給子頁(iframe.tw),讓子頁內容變色。

Demo 的頁面在這:

https://letswritetw.github.io/letswrite-postMessage/

Demo 頁的原始碼也放在 GitHub 上了:

https://github.com/letswritetw/letswrite-postMessage

Summary
postMessage:主頁、iframe 頁可互相傳值
Article Name
postMessage:主頁、iframe 頁可互相傳值
Description
本篇大綱:前端的一個困擾 管不到 iframe。Google、Facebook 也是這樣傳值的。postMessage 基本使用、傳值、接值。原始碼、Demo。本篇筆記要記錄的就是 A、B 頁面 iframe 的情況下,A 傳給 B,或 B 傳給 A 的問題怎麼解決。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg

隨選筆記文

API Front-End

用 Google Apps Script 寫一個 LINE 登入功能:上篇 – 前置作業

Front-End

製作 RWD email 工具:MJML,如何使用及注意事項

Front-End

用 JavaScript 監聽頁面 / 頁籤被關閉的事件

WordPress

用 Auth0 在 WordPress 上做會員註冊、登入功能

Vue

Nuxt.js 引用 Firebase SDK

Front-End

用純 CSS 寫的網頁預覽效果

Forms Google

Google 表單 用網址改變題目預設值

Google Maps

Google Maps API 學習筆記 – 5:抓目前位置、計算到各點距離

Bot Telegram

在網頁上嵌入Telegram 頻道廣播訊息

Bot LINE

用 LINE Push Messaging API 推播每日氣象預報

以下是留言,但關於留言的部份必需先讓你們知道:

本站的文章都是 August 因為覺得有趣,才會實作並整理成筆記文而後進行發表。

如果留言是希望把 Demo 改成「你想要」的樣子,或是把功能改成「符合你需求」的樣子,

Sorry~ 除非那修改是 August 也有興趣的,不然不會幫你們寫程式去面對工作或是交作業。

未來這類的留言不會再主動回覆。😎

另外,公開信箱是為了讓金流驗證用,

因為之前遇過幾次回信協助解決問題後,對方卻一聲謝謝也沒有,就這樣拿去幫工作交差。

因此決定不再回覆信件,有疑問就利用留言功能囉。