0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

iframe 自動適應高度怎麼做?同網域與跨網域的寫法(含 postMessage 範例)

iframe自動適應高度

用 iframe 嵌入另一個網頁時,常遇到這個問題:iframe 的高度是固定的,裡面的內容比較長就出現一條捲軸,內容比較短又留下一大片空白。

iframe 不會自動跟著內容長高,因為 iframe 裡的網頁是一份獨立的文件,外層網頁在排版時並不知道它有多高。沒有設定高度時,瀏覽器預設只給 150px。

要讓 iframe 自動適應高度,先看你是哪一種情況:

  1. 內容是固定比例的(影片、地圖):用 CSS 的 aspect-ratio,不需要 JavaScript。
  2. iframe 裡的網頁跟外層是同一個網域:外層用 JavaScript 讀取內容的高度。
  3. iframe 裡的網頁是別的網域:要在 iframe 裡的網頁加一段程式,用 postMessage 把高度傳出來。

先加上這段基本 CSS

CSS
.auto-frame {
  display: block; /* 避免 iframe 下方多出一條小縫 */
  width: 100%;
  border: 0;
}

情況一:影片、地圖這類固定比例的內容

YouTube、Google 地圖這類內容沒有固定的「內容高度」,而是會填滿整個 iframe,所以只要固定寬高比例就好:

CSS
.video-frame {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
}

詳細說明請看iframe 嵌入網頁語法。

情況二:同網域,用 JavaScript 讀取內容高度

「同網域」指的是協定、網域、連接埠都相同,例如在 https://www.example.com/ 的頁面裡嵌入 https://www.example.com/news/list.html。要注意 example.com 和 www.example.com 算不同網域,http 和 https 也算不同。

同網域時,外層網頁可以直接讀到 iframe 裡的內容高度:

HTML
<iframe id="news-frame" class="auto-frame" src="/news/list.html" title="最新消息"></iframe>

<script>
  const newsFrame = document.getElementById('news-frame');

  newsFrame.addEventListener('load', () => {
    const doc = newsFrame.contentDocument;
    if (!doc) return; // 跨網域時會是 null,請改用情況三的做法

    const fit = () => {
      const height = doc.documentElement.getBoundingClientRect().height;
      newsFrame.style.height = Math.ceil(height) + 'px';
    };

    fit();

    // 內容有變化時 (圖片載入、展開收合) 重新調整高度
    // 這裡使用 iframe 裡那份網頁的 ResizeObserver
    const Observer = newsFrame.contentWindow.ResizeObserver;
    new Observer(fit).observe(doc.documentElement);
  });
</script>

幾個重點:

  • 這段 script 要放在 iframe 的後面,確保 iframe 載入完成時已經在監聽。
  • ResizeObserver 會在 iframe 裡的內容大小改變時通知,例如圖片慢慢載入、使用者展開收合選單,高度都會跟著更新。
  • iframe 裡點連結換頁時會再觸發一次 load,新的頁面也會自動調整。

量出來的高度不對?

  • 子頁面的 CSS 如果把 html、body 設成 height: 100%,量到的會是 iframe 目前的高度,而不是內容的高度,要把這個設定拿掉。
  • 子頁面裡有用 vh 單位設定高度的元素時,iframe 變高,vh 也跟著變大,可能會越長越高。會被放進 iframe 的頁面,盡量不要用 vh 設定高度。

情況三:跨網域,用 postMessage 傳遞高度

iframe 裡是別的網域時,瀏覽器的同源政策不允許外層讀取裡面的內容,情況二的 contentDocument 會是 null。

這時唯一的辦法,是請 iframe 裡的網頁自己回報高度:子頁面量好高度,用 postMessage 傳給外層,外層收到後再調整 iframe。所以這個方法的前提是:你能修改 iframe 裡的網頁(例如自己另一個網域的網站),或對方網站有提供這種功能。

子頁面(被嵌入的網頁)

放在子頁面的最後面,把 https://www.parent-site.com 換成外層網站的網域:

HTML
<script>
  const PARENT_ORIGIN = 'https://www.parent-site.com';

  if (window.parent !== window) {
    const sendHeight = () => {
      const height = Math.ceil(document.documentElement.getBoundingClientRect().height);
      window.parent.postMessage({ type: 'frame-height', height: height }, PARENT_ORIGIN);
    };

    new ResizeObserver(sendHeight).observe(document.documentElement);
  }
</script>
  • ResizeObserver 開始監看時會先通知一次,之後內容高度每次改變都會再送出。
  • postMessage 的第二個參數是「只把訊息送給這個網域」。子頁面會被好幾個網站嵌入時,才考慮改成 '*'(高度不是機密資料,但不要用這種方式傳送其他資料)。

外層網頁

把 https://www.child-site.com 換成子頁面的網域。這段程式放在 iframe 的前面或緊接在後面,才不會漏接子頁面第一次送出的高度:

HTML
<iframe id="partner-frame" class="auto-frame" src="https://www.child-site.com/form.html" title="線上報名表"></iframe>

<script>
  const CHILD_ORIGIN = 'https://www.child-site.com';
  const partnerFrame = document.getElementById('partner-frame');

  window.addEventListener('message', (event) => {
    // 只接受子頁面網域、而且是這個 iframe 送來的訊息
    if (event.origin !== CHILD_ORIGIN) return;
    if (event.source !== partnerFrame.contentWindow) return;

    const data = event.data;
    if (!data || data.type !== 'frame-height') return;

    const height = Number(data.height);
    if (Number.isFinite(height) && height > 0) {
      partnerFrame.style.height = Math.ceil(height) + 'px';
    }
  });
</script>

檢查 event.origin 非常重要:任何網頁都能對你的網頁送 postMessage,不檢查來源的話,別人可以送奇怪的資料進來。收到的高度也要先確認是正常的數字,才拿來使用。

需要處理更多狀況時

多個 iframe、iframe 裡的錨點連結、捲動位置同步等等,要處理的細節會越來越多,這時可以考慮使用現成的開源套件(例如 iframe-resizer)。使用前請先確認它的授權條款是否適用你的網站。

舊版程式碼為什麼不能用了?

這篇文章舊版的程式碼,是在 iframe 載入時讀取 contentDocument.body.offsetHeight 設定高度,裡面還有判斷 window.opera、Document 這些給十幾年前的 Opera 與 IE 瀏覽器用的寫法。問題是:

  • 只在載入時量一次,之後圖片載入、內容變動都不會更新。
  • body.offsetHeight 不包含 body 的外距(margin),常常差幾個像素,又多出一條捲軸。
  • 跨網域時完全無效,而且沒有任何錯誤提示,很難發現問題出在哪裡。

用上面情況二或情況三的寫法取代即可。

其實不一定要用 iframe

如果嵌入的是自己網站的內容(例如最新消息列表),與其用 iframe 再想辦法調整高度,不如直接把內容放進頁面裡,例如用 PHP 的 include,或網站系統本身的區塊、短代碼功能。這樣沒有高度問題,搜尋引擎也會把這些內容當成這一頁的一部分;放在 iframe 裡的內容,搜尋引擎不一定會這樣處理。

延伸閱讀

把訂位系統、報名表這類外部工具嵌進官網時,高度與手機版面往往是最花時間調整的地方。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與 RWD 響應式網站,歡迎來聊聊。

分享這篇文章

返回文章列表更多「html網頁語法教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!