最近在支援公司某項專案時,碰到了需要在頁面導入不同源頁面的狀況,在導入過程中由於不知道其高度,直覺上就打算使用 DOM Selector
去取 iframe
內的元素:
const iframe = document.querySelector("iframe");
iframe.onload = () => {
const iframeInner = iframe.contentWindow;
console.log(iframeInner);
};
查了一下才知道,基於同源政策,如果透過 iframe
去引入不同源的頁面,我們是不能透過 JavaScript
去存取其中的任何東西的。
光就高度而言,用 F12
直接去看該頁面的高度,並直接用 CSS
給一個固定高度好像也行吧?但 iframe
頁面的高度如果在未來的某個版本改變了,導入 iframe
的 parent
頁就也必須跟著改,那有沒有什麼方法,可以讓 iframe 在被引入時傳遞一些 parent 頁所需資訊的方法嗎?
postMessage
如果 iframe
的 src
為不同源(http
和 https
、不同的 Port
、不同的 Domain
),彼此之間就可以使用 window.postMessage
來溝通。
假設我現在有一個 Port
為 5501
的 parent.html
及 Port
為 5500
的 iframe.html
,我們可以在 iframe.html
中輸入:
parent.postMessage("some message", "http://127.0.0.1:5501/parent.html");
在 parent.html 中輸入:
window.addEventListener("message", (e) => {
console.log(e.data);
});
應該可以在 parent.html
中看見 iframe.html
傳入的 ‘some message’
字串。
在 postMessage
第一個參數 message
除字串外也可以傳入其他型別的值,如果要傳遞高度:
parent.postMessage({ height: 550 }, "http://127.0.0.1:5501/parent.html");
在 parent
就可以將其設定在 iframe
的高度:
window.addEventListener("message", (e) => {
iframe.style.height = e.data.height + "px";
});
安全性問題
看到這裡可能會發現,透過這樣的傳遞方式雖然方便,但在安全性一定也會有問題。例如這個 parent.html
接受數個不同源 iframe
,就可能導致一些問題。我們可以透過 event.origin
讓 message
的 function
決定要不要做事情:
window.addEventListener("message", (e) => {
const origin = e.origin;
if (origin === "http://127.0.0.1:5500") {
iframe.style.height = e.data.height + "px";
} else {
// ...
}
});
同理,在 iframe.html
中,postMessage
也能夾帶 origin
去決定哪個源可以接受要傳遞的值。
其他運用
說來比較可惜,由於目前支援的專案,其不同源的 iframe
中內容會基於使用者的一些操作去和後端伺服器交換資訊,如果使用 postMessage
,就可以讓 iframe
把內容傳遞給 parent
(也就是該專案本體),再由 parent
去和後端伺服器交換資訊後決定要做什麼事情,相較讓 iframe
中的頁面去做會方便許多,彈性一定也會更大。接觸到 postMessage
的時機點已經算是專案接近尾聲的時候了,但在規劃階段我們就已經商議出其他替代性的做法,因此就沒辦法在實務上用到這個 API
了。
關於 iframe
與 parent
運用 postMessage
傳遞資訊的筆記就記錄到這邊,內文如果有任何錯誤,還請不吝指出,謝謝大家。
References: