はじめに
ロカオプの縣です。一番好きなJavaScriptの関数はpostMessageです!
ちょい前にiframeでの埋め込みでスクロールバーを出さずにピッタリの高さに中身とiframeの高さを同期が必要な要件がありました。 その際にResizeObserverとpostMessageを使ってうまく実装ができたので、その方法をご紹介します。
iframeの高さ調整問題とは?
ここでは便宜的に以下のように呼びます。
- 親ページ(https://parent.example.com/parent.html):iframeを使って他のページを埋め込む側のページ
- 子ページ(https://child.example.com/child.html):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技術を適材適所で活用しサービスを開発しています。 ロカオプのエンジニアリングに興味がある方は、ぜひ以下もご覧ください。
*1:昔、ResizeObserverがない時代は300msごとにサイズをチェックするなどの対応が必要だったので、大変よい時代になりました。