跳到主要內容
← 回到學習

學習 · 前端基礎

iframe 嵌入

目錄 · 7 個段落
  1. 01基本使用
  2. 02痛點 1:高度自適應
  3. 03痛點 2:跨域通訊 — postMessage
  4. 04痛點 3:scroll bar 雙重出現
  5. 05安全性:sandbox 屬性
  6. 06防止自己被別人嵌入
  7. 07何時 / 不該用 iframe

iframe 嵌入

TL;DR
iframe 兩大痛點:無法自動抓內容高度 + 跨域時無法直接存取 DOM。前者用 iframe-resizer 解,後者用 postMessage 解。

基本使用

<iframe
  src="https://example.com"
  width="100%"
  height="500"
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
  sandbox="allow-scripts allow-same-origin"
></iframe>
html
屬性建議
  • loading="lazy" — iframe 進入視窗才載入,省巨量頻寬
  • frameborder 已 deprecated,改用 CSS border: 0
  • sandbox 列白名單,不寫就完全限制(連 JS 都不能跑)

痛點 1:高度自適應

原生 iframe 無法自動調整高度 —— 因為瀏覽器要保護跨域內容,父頁面看不到 iframe 內部 DOM 高度。

同源情況(可以自己讀高度)

const iframe = document.querySelector('iframe');
iframe.addEventListener('load', () => {
  const height = iframe.contentWindow.document.body.scrollHeight;
  iframe.style.height = height + 'px';
});
javascript

跨域情況(必須用套件 / postMessage)

直接讀 contentWindow.document 會被 Same-Origin Policy 擋下。解法:

React 推薦套件:iframe-resizer-react

import IframeResizer from 'iframe-resizer-react';
 
<IframeResizer
  src="https://example.com"
  style={{ width: '100%', minHeight: '500px' }}
  checkOrigin={false}    // 跨域時設 false
/>
tsx

運作原理:套件會在 iframe 內注入一個 script,內部監聽尺寸變化後用 postMessage 通知父頁面更新高度。


痛點 2:跨域通訊 — postMessage

iframe 內外無法直接互相讀 DOM,但可以用 postMessage 安全傳訊息。

父頁面 → iframe

const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage(
  { type: 'INIT', payload: { theme: 'dark' } },
  'https://target-origin.com'    // ⚠️ 永遠寫明確 origin,別用 '*'
);
javascript

iframe → 父頁面

window.parent.postMessage({ type: 'READY' }, 'https://parent-origin.com');
javascript

雙方接收

window.addEventListener('message', (event) => {
  // ⚠️ 必須驗證來源,否則任何人都能傳訊息進來
  if (event.origin !== 'https://trusted-origin.com') return;
  if (event.data.type === 'READY') {
    // ...
  }
});
javascript
安全鐵則
  1. 永遠驗證 event.origin — 不驗證 = 任何網站都能傳惡意訊息
  2. 發送時寫明 targetOrigin — 用 '*' 等於把訊息廣播給任何能載入該 iframe 的網站
  3. 永遠驗證資料結構 — 收到的 event.data 可能是任何東西,要驗 schema

痛點 3:scroll bar 雙重出現

iframe 內容超出時會出現自己的 scroll bar,父頁面也可能出現 → 雙 scroll bar 體驗很糟。

解法

<iframe scrolling="no" style="overflow: hidden;"></iframe>
html

搭配 iframe-resizer 自動調高度,iframe 內就不會超出 → 沒 scroll bar。


安全性:sandbox 屬性

預設 iframe 是「全權限」(可以執行 JS、開新分頁、讀 cookies 等)。sandbox 開白名單模式:

<iframe sandbox></iframe>                                <!-- 完全限制 -->
<iframe sandbox="allow-scripts"></iframe>                <!-- 只允許 JS -->
<iframe sandbox="allow-scripts allow-same-origin"></iframe>  <!-- 加上同源 -->
html
常用值允許
allow-scripts執行 JS
allow-same-origin視為同源(才能讀自己 cookies / localStorage)
allow-forms提交表單
allow-popups開新視窗 / target=_blank
allow-top-navigation改變父頁面 URL(少用,易被濫用 )
矛盾組合
同時開 allow-scripts + allow-same-origin 等於完全沒沙盒效果(因為內部 JS 可以呼叫 parent.postMessage(...) 後讀 cookies)。實務上要互信才這樣設。

防止自己被別人嵌入

如果你不希望別人用 iframe 嵌入你的網站(防 clickjacking):

# 舊寫法
X-Frame-Options: DENY
 
# 現代寫法(更彈性)
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com
http

何時 / 不該用 iframe

適合
  • 嵌入第三方內容:YouTube、Google Maps、CodePen
  • legacy 系統包裝:把舊系統當「黑盒子」嵌進新系統
  • 沙盒執行使用者程式碼:Codepen / StackBlitz 模式
不適合
  • 同站元件複用 — 用 React 元件就好
  • SEO 重要的內容 — Google 對 iframe 內容索引有限
  • 行動裝置主要互動 — touch 事件穿透問題多

目錄