ロカオプ技術ブログ

株式会社ロカオプの技術ブログです。

ResizeObserverとpostMessageを使ってクロスドメイン間でコンテンツの高さを同期する

はじめに

ロカオプの縣です。一番好きなJavaScriptの関数はpostMessageです!

ちょい前にiframeでの埋め込みでスクロールバーを出さずにピッタリの高さに中身とiframeの高さを同期が必要な要件がありました。 その際にResizeObserverとpostMessageを使ってうまく実装ができたので、その方法をご紹介します。

iframeの高さ調整問題とは?

iframeの高さ調整問題

ここでは便宜的に以下のように呼びます。

上記の場合、子ページの高さが可変の場合、iframeの高さを決め打ちで設定していると縦方向のスクロールバーが表示されてしまいます。

親ページ側で子ページのコンテンツの高さが分かればよいのですが、クロスドメインの制約によりiframe内の要素には親フレームからはアクセスができません。

デスクトップとモバイルで余裕を持った高さをなんとなくiframeに設定するというのがよくある回避策ではないかと思います。

この問題はResizeObserverとpostMessageを使うといい感じにクリアできます。以下がその方法です。

1. 子ページの高さが変わったことを子ページ内で検知する

以下のようにResizeObserverを使うことで簡単にサイズ変更を検知できます。*1

子ページ

const resizeHandler = () => {
  console.log('現在の高さ: ' + document.documentElement.offsetHeight);
};

const setup = () => {
  //  iframe内にいるかどうかのチェック
  if (window.parent === window) {
    // iframe内ではない場合は何もしない
    return;
  }
  const resizeObserver = new ResizeObserver(resizeHandler);
  // ドキュメントのサイズ変更を監視
  resizeObserver.observe(document.documentElement);
};


setup();

2. 子ページの高さが変わったら親ページにメッセージを送る

ResizeObserverで検知した高さの変更をpostMessageを使って親ページにメッセージ送信します。具体的にはwindow.parent.postMessageで親ページにメッセージを送ることができます。 postMessageで送るメッセージは任意の形式を使えますがここではメッセージの種別をtype、現在の高さをheightとして送っています。

子ページ

const resizeHandler = () => {
  // 親ページにメッセージを送信
  window.parent.postMessage(
    {
      type: 'document-height-changed',
      height: document.documentElement.offsetHeight,
    },
    'https://parent.example.com'
  );
};

export const setupWidgetMode = () => {
  //  iframe内にいるかどうかのチェック
  if (window.parent === window) {
    // iframe内ではない場合は何もしない
    return;
  }
  const resizeObserver = new ResizeObserver(resizeHandler);
  // ドキュメントのサイズ変更を監視
  resizeObserver.observe(document.documentElement);
};

setup();

3. 親ページでメッセージを受け取りiframeの高さを変更する

window.addEventListener('message',でpostMessageで送られてきたメッセージを受信して、高さの変更を伝えるメッセージであればiframeの高さを変更します。 これにより、子ページの高さとiframeの高さが常に同期するようになります。

親ページ

<iframe id="iframe" src="https://child.example.com/child.html"></iframe>
<script type="text/javascript">
  window.addEventListener('message', (event) => {
  // メッセージの送信元オリジンをチェック
  if (event.origin !== 'https://child.example.com') {
    return;
  }
  if (event.data.type === 'document-height-changed') {
    if (event.data.height) {
      // 最新の高さをiframeに設定
      document.querySelector('#iframe').style.height = event.data.height + 'px';
    }
  }
}) 
</script>

まとめ

  • 要素のサイズ変更を検知するにはReizeObserverが便利
  • クロスドメイン間でのメッセージのやりとりはpostMessageが便利
  • この2つを組み合わせればiframeの高さ調整問題を解決できる

おわりに

ロカオプではこのようにWeb技術を適材適所で活用しサービスを開発しています。 ロカオプのエンジニアリングに興味がある方は、ぜひ以下もご覧ください。

tech.locaop.jp

*1:昔、ResizeObserverがない時代は300msごとにサイズをチェックするなどの対応が必要だったので、大変よい時代になりました。