このすみノート

Webエンジニアが技術や趣味を書くブログです。

JavaScriptでiframeとやりとりするためにpostMessageを使う

  • 仕事での話ですが、とある画面からロードしたiframeと呼び出し元との間で、イベントをやり取りしたいケースがありました。
  • これがなかなかに上手く行かなかったので、最終的にどう解決したのか共有します。

イベントのやり取りが出来なかった理由

  • クロスドメインの制約に引っかかったからです。
  • 原因はiframeの呼び出し元画面と、iframe側のドメインが異なることにありました。
  • サブドメイン(aa.example.comとbb.example.com)間だったのですが、それでもダメでした。

stackoverflow.com

Window.postMessage()

  • 最終的に行き着いたのが、window.postMessage() です。
  • MDNの解説を読むと、オリジン間通信を可能にすると記されています。

window.postMessage() は、 Window オブジェクト間で安全にオリジン間通信を可能にするためのメソッドです。例えば、ポップアップとそれを表示したページの間や、iframe とそれが埋め込まれたページの間での通信に使うことができます。
https://developer.mozilla.org/ja/docs/Web/API/Window/postMessage

構文について

postMessage には構文が2つあります。 私は前者を使いました。

  1. postMessage(message, targetOrigin)
  2. postMessage(message, targetOrigin, transfer)

MDNのサンプル

/*
 * In window A's scripts, with A being on http://example.com:8080:
 */

const popup = window.open(/* popup details */);

// When the popup has fully loaded, if not blocked by a popup blocker:

// This does nothing, assuming the window hasn't changed its location.
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// This will successfully queue a message to be sent to the popup, assuming
// the window hasn't changed its location.
popup.postMessage("hello there!", "http://example.com");

window.addEventListener("message", (event) => {
  // Do we trust the sender of this message?  (might be
  // different from what we originally opened, for example).
  if (event.origin !== "http://example.com")
    return;

  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"
}, false);

targetOriginについて

  • targetOriginには、https://aa.example.com といった、イベント配信するウィンドウのオリジンを指定します。
  • * で制限しないことも可能ですが、安全性が要求されるメッセージを通信するケースでは、常に指定することが推奨されます。

メッセージを受け取る側

  • メッセージイベントを、addEventListenerでリッスンします。
  • 実際には、受信したメッセージ内容によって、具体的な処理を書き分ける(分岐)ことになると思います。
// iframe呼び出し元は、自分自身のイベントをリッスンしている
window.addEventListener("message", (event) => {
  if (event.origin !== "https://example.com")
    return;

  // ...
}, false);

メッセージを送信する側

postMessageで、メッセージを送信します。

// iframeから呼び出し元の画面へ、オリジン間通信メッセージを送信する
window.parent.postMessage("hello world", "https://example.com");

まとめ

  • window.postMessage() によって、オリジン間通信できることがわかりました。
  • ドメインが異なるポップアップや、iframeとやり取りしたいケースで有用です。
  • (今回は業務都合でiframeとやりとりすることになったのですが、ブラウザのCORSは厳しいので、使わなくて済むのであれば使いたくないのが本音だったりします)

参考サイト様 - ウィンドウを跨いだやり取り

ja.javascript.info