iframeで埋め込み元の高さ計算して埋め込み先に反映させる方法

HTMLのインラインフレーム要素(iframe)で、他サイトのページを埋め込むアイデアはわりとよく使われます。TwitterやFacebookのウィジェットなんかも、iframeで埋め込んでいますよね。

私たちが使う場合では、異なるサービス間でコンテンツをやり取りしたい場合でしょうか。
たとえば、カラーミーショップ(EC)にWordPressのコンテンツを埋めこんだり。

やってみると意外に難しい場面に遭遇しますが、その点について解説します。

iframe埋め込みで注意すること

埋め込む先が常時SSLの場合は、埋め込み元サイトもSSL化しておく必要があります。

サーバー設定によっては、iframeで埋め込めないこともあります。

昨今はレスポンシブサイトに埋め込むことが多いと思います。横幅に対してコンテンツが可変しますので、埋め込みページの高さが増減することがあります。
また動的なコンテンツを埋め込む場合も高さは変化します。

高さが変化しないような埋め込みは高さを固定で指定して問題ありません。
高さが変化する場合は下が切れたりするので、高さ調整を行う必要があります。
その部分の作りが、初心者の方にとってはむずかしいです。

コード解説

埋め込み元、埋め込み先、どちらにもコードを書きます。

埋め込み元

コード

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

<script>
$(window).on('load resize', function(){
  setTimeout(function(){
    var height = $('body').height();
    window.parent.postMessage(height, 'https://埋め込み先.com');
  },500);
});
</script>

埋め込み元では、$('body').height() で高さ計算します。

window.postMessage() で埋め込み先に高さを送信します。Window オブジェクト間で安全にクロスドメイン通信を可能にするためのメソッドです。

setTimeout() は、高さ計算前にどの程度待ち時間を入れるかです。
動的なコンテンツの場合、待ち時間がある程度ないと正確な高さが取得できません。
ゼロで問題ない埋め込みもありますので、各自で調整してください。

埋め込み先

コード

<div>
  <iframe style="width:100%; height:1px; display:block; margin: auto auto; border:none;" class="child-iframe" src="https://埋め込み元.com/contact.html" scrolling="no"></iframe>
</div>

<script>
$(function(){
  $(window).on('message', function(event){
    // 末尾にスラッシュをつけるとエラーになるので注意
    if (event.originalEvent.origin != 'https://埋め込み元.com') return;

    $('.child-iframe').css({
      'width': '100%',
      'height': event.originalEvent.data
    });
  });
});
</script>

上記のサンプルコードの、iframeのsrc部分 https://埋め込み元.com/contact.html(埋め込むページURL)、event.originalEvent.originの https://埋め込み元.com(ドメイン)の二か所は任意の文字列になります。書き換えてお使いください。

$(window).on('message')で、window.postMessage()で送信したメッセージを受け取っています。
その後、css()メソッドで、iframeの高さをセットしています。

event.originalEvent.originには送信元が入っていて、正しい送信元かチェックしています。このチェックがないと、全然関係ないサイトからでも埋め込み先にメッセージを送れるんですね(セキュリティ上の問題が発生する)。

うまく動作しない場合はconsole.log()を使って、if文がうまく条件クリアできているかを確認し、さらにheightの計算が正常に終わっているかを見て、setTimeout()の待ち時間を調整すればよいです。

おわりに

iframeで埋め込みって、機能設置の際に意外とよく検討します。
読み込みに少し時間が必要なので、別案があるときはそちらも併せて検討します。

あたまに入れておくと、Webサイト制作の役に立ちます。

追記

こちらはモダンブラウザ用で、
Promise(読み込みを待ってから高さを計測)、
async・await(高さ計算関数の処理を待ってから送信)で、実行順を制御します。

setTimeout の代わりに requestAnimationFrameに変更、その他非同期処理をつかったりする版。
デバウンス化は、リサイズ・イベント時に連続で処理が発生しないようにするための対応になります。
こちらのほうが、よりしっかりとした処理をします。

埋め込み元

// 埋め込み元
// 高さを計算して返すPromise関数
function calculateHeight() {
  return new Promise((resolve) => {
    // DOMの更新が完全に完了するまで待機
    requestAnimationFrame(() => {
      const height = document.body.offsetHeight;
      resolve(height);
    });
  });
}

// リサイズ処理をデバウンス化する関数
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

// 高さを送信する関数
async function sendHeight() {
  try {
    const height = await calculateHeight();
    window.parent.postMessage(height, 'https://埋め込み先.com');
  } catch (error) {
    console.error('高さの計算・送信に失敗:', error);
  }
}

// イベントリスナーの設定
const debouncedSendHeight = debounce(sendHeight, 500);
window.addEventListener('load', sendHeight);
window.addEventListener('resize', debouncedSendHeight);

埋め込み先

// 埋め込み先
document.addEventListener('DOMContentLoaded', () => {
  window.addEventListener('message', (event) => {
    // オリジン検証
    if (event.origin !== 'https://埋め込み元.com') return;

    const iframe = document.querySelector('.child-iframe');
    if (!iframe) return;

    try {
      iframe.style.width = '100%';
      iframe.style.height = '${event.data}px';
    } catch (error) {
      console.error('iframeのリサイズに失敗:', error);
    }
  });
});

執筆者

えいじ@naeco.jp この記事を書いた人

メーカー系情報システム部門出身の個人事業主。
自作するのが好きですぐに試したくなる、凝り性なWebエンジニア。
カラーミーショップ、モールなどのECについて記事にしています。

ご相談・お問い合わせはこちら