iframe 嵌入
iframe 嵌入
TL;DRiframe 兩大痛點:無法自動抓內容高度 + 跨域時無法直接存取 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>
屬性建議
loading="lazy"— iframe 進入視窗才載入,省巨量頻寬frameborder已 deprecated,改用 CSSborder: 0sandbox列白名單,不寫就完全限制(連 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';
});
跨域情況(必須用套件 / 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
/>
運作原理:套件會在 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,別用 '*'
);
iframe → 父頁面
window.parent.postMessage({ type: 'READY' }, 'https://parent-origin.com');
雙方接收
window.addEventListener('message', (event) => {
// ⚠️ 必須驗證來源,否則任何人都能傳訊息進來
if (event.origin !== 'https://trusted-origin.com') return;
if (event.data.type === 'READY') {
// ...
}
});
安全鐵則
- 永遠驗證
event.origin— 不驗證 = 任何網站都能傳惡意訊息- 發送時寫明 targetOrigin — 用
'*'等於把訊息廣播給任何能載入該 iframe 的網站- 永遠驗證資料結構 — 收到的
event.data可能是任何東西,要驗 schema
痛點 3:scroll bar 雙重出現
iframe 內容超出時會出現自己的 scroll bar,父頁面也可能出現 → 雙 scroll bar 體驗很糟。
解法
<iframe scrolling="no" style="overflow: hidden;"></iframe>
搭配 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> <!-- 加上同源 -->
| 常用值 | 允許 |
|---|---|
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
何時 / 不該用 iframe
適合
- 嵌入第三方內容:YouTube、Google Maps、CodePen
- legacy 系統包裝:把舊系統當「黑盒子」嵌進新系統
- 沙盒執行使用者程式碼:Codepen / StackBlitz 模式
不適合
- 同站元件複用 — 用 React 元件就好
- SEO 重要的內容 — Google 對 iframe 內容索引有限
- 行動裝置主要互動 — touch 事件穿透問題多