uhyohyo.net

JavaScript初級者から中級者になろう

十三章第三回 Web Messaging

このページの最終更新日:

第十三章のテーマは「通信」でしたね。次に紹介するのはWeb Messagingです。前回と前々回ではHTTP通信にかかわる機能を紹介しましたが、今回のこれはHTTP通信はしません。

それではどのような通信かというと、これはブラウジングコンテキスト間の通信です。

ブラウジングコンテキストとは何かというと、これはブラウザのタブの1つ1つのことです。つまりブラウジングコンテキストで通信するということは、複数のタブの間でデータをやりとりするということです。

ただし、ここでいう複数のタブというのは同じ人の、同じブラウザのタブです。Web Messagingはインターネット上を通信するものではなく、あくまで各個人のPCやスマートフォン等の中で、異なるタブの間でデータをやり取りできるということです。その意味では、前回までとはだいぶ毛色が異なりますね。

とはいえ、他のブラウジングコンテキストとデータをやりとりするということは、他にどんなタブが開かれているのかを知る必要があるわけですが、ユーザーがどんなページを今閲覧しているかというのは重要な個人情報ですので、そんなものはJavaScriptからは取得できません。それではどうすればいいのでしょうか、というところから今回の話が始まります。

JavaScriptから他のブラウジングコンテキストを操作する

JavaScriptから他のブラウジングコンテキストを知る方法のひとつは、JavaScriptから新しいウィンドウを開くことです。最近のブラウザではウィンドウもタブの一種ですので、ここでブラウジングコンテキストが誕生します。自分で作ったブラウジングコンテキストなら知っているだろうという寸法です。

JavaScriptからウィンドウを開くには、window.openメソッドを使います。

window.openメソッドの第一引数はURLです。第二引数はウィンドウの名前です。ウィンドウに名前を設定すると、同じ名前のウィンドウは1つしか作れなくなります(詳しくは後述)。また、第三引数にオプションを指定することもできますが、ここでは詳しくは解説しません。

それではwindow.openのサンプルです。


window.open("https://www.google.co.jp/","google");

を押すと上のプログラムが動作します。

window.openを実行すると、大抵の場合は新しいタブが開かれます。新しいウィンドウと上では言いましたが、最近のブラウザは本当に新しいウィンドウを作るのではなくタブを作るだけにとどめることがあるようです。このタブには内部的に第二引数の"google"という名前がつけられます。

ここでそのタブが開いた状態で再びボタンを押すと、新しいタブは開きません。これは、"google"という名前のウィンドウがすでにあるため、それが再利用されたからです。もしその"google"のタブを消してからボタンを押したならば、また新しいタブが開くでしょう。

ちなみに、最近のブラウザには「ポップアップブロック」機能がついています。これは、JavaScriptでwindow.openで勝手に広告のウィンドウを出したりするのが邪魔くさいのでそういったものを動作させないようにする機能です。ですから、「ページと同時に新しいウィンドウを開く」というプログラムを作ったりしてもポップアップブロックに引っかかってウィンドウが開いてくれない可能性が高いです。

今回のサンプルはブロックされないブラウザが多いとおもいますが、これは「ボタンをクリックする」というユーザーの動作がトリガーになったせいでしょう。

さて、そして、このwindow.openの戻り値はWindowオブジェクトです。このWindowオブジェクトが、その開かれたウィンドウのブラウジングコンテキストを表すオブジェクトです。

お察しの通り、今開いているページのブラウジングコンテキストに対応するのがおなじみのwindowです。つまり、window.openによって開いたタブについては、そのページに対応するWindowオブジェクトを得ることができるのです。

また、今まで慣れ親しんできたdocumentもwindowのプロパティです。以前述べたようにwindowのプロパティはグローバル変数として利用可能なので我々はwindow.documentをdocumentとして扱ってきたのです。

window.document === document	//true

つまり、今回得られたWindowオブジェクトのdocumentプロパティを通じて開いたタブの中身を操作できるかもしれません。

ところが、第13章からおなじみの同一オリジンポリシーはここでも有効です。異なるオリジンのページの情報は取得できず、当然操作することもできません。

それでは同じオリジンなら操作できるのでしょうか。試してみましょう。下に2つのボタンを用意しました。それぞれ以下のような動作です。


  //ボタン1(ウィンドウを開く)
  win=window.open("/");

  //ボタン2(ウィンドウの中身を書き換える)
  win.document.body.textContent="書き換えました";
  

ボタン1を押したあとボタン2を押してみましょう。開いたウィンドウの中身が書き換えられたことが確認できたと思います。

これが2つのウィンドウの連携の基本ですが、このように外からページを操作するというのはあまりスマートではありません。ではどのような方法がスマートなのかというと、外から与えるのは「指示」だけで、実際の動作は内部で行われるようなのがいいでしょう。

この方法の利点は、複数の異なるページから、同じページをウィンドウとして開いて同じ動作をさせるとき、外からいじるのでは同じソースを操作元の両方のページで読みこませる必要があるのに対して、指示を与える方法ならば実際に動作するソースは操作される側のページ1つだけで済むということです。

それで、その指示を与える方法というのが、今回紹介するWeb Messagingなのです。

メッセージを送る

さて、あるブラウジングコンテキストに対してメッセージを送るには、WindowオブジェクトのメソッドであるpostMessageを使います。これを呼び出すとそのウィンドウに対してメッセージを送ります。

引数は、第1引数が送るメッセージです。送ることができるメッセージは、プリミティブ(文字列とか数値とか真偽値とか)や、普通のオブジェクト、配列、Dateオブジェクトなどです。また、最近よく出てくるFile, Blob, FileList, ArrayBufferも送ることができます。

さらに、第2引数は送る先のオリジンです。これは、意図しないサイトに向けて情報を漏らしてしまわないように、メッセージ送る先のオリジンを指定できるということです。送る先のブラウジングコンテキストのオリジンが指定したものと異なる場合はメッセージの送信は失敗します。

例えばhttps://www.google.com/以下のページに送りたい場合は、


postMessage("メッセージ","https://www.google.com");

のようにします。この第2引数は省略できません。オリジンの他に指定できる特殊な値としては、現在のオリジンと同じオリジンを表す"/"と、送る先がどのオリジンでもいいことを表す"*"があります。

ここで注目すべきことは、メッセージを他のオリジンに送ることもできるということです。メッセージを送りつけるだけなら、同一オリジンポリシーに縛られずに送ることができるわけです。

メッセージを受け取る

さて、メッセージを送られたら、送られた側では当然受け取る仕組みが必要になります。それはやはりイベントです。メッセージを受け取った場合、windowでmessageイベントが発生します。したがって、次のようにメッセージを検知できます。


window.addEventListener("message",function(e){
});

メッセージの中身を参照するのに使うのは当然イベントオブジェクトを使います。ここで、このmessageイベントのイベントオブジェクトをMessageEventといいます。

前回の内容を覚えているでしょうか。EventSourceにおいてサーバーからのメッセージを受け取る時のイベントオブジェクトもMessageEventでした。同じですね。なので、メッセージの取得方法も前回と似ています。すなわち、送られてきたメッセージはdataプロパティに入っています。


window.addEventListener("message",function(e){
  console.log("送られてきたメッセージは",e.data);
});

MessageEventにはoriginプロパティもありましたね。これはWeb Messagingの場合は、このプロパティでメッセージの送信元のページのオリジンを知ることができます。前回あったlastEventIdはServer-Sent Events用なので、今回は意味がありません。

もう一つsourceプロパティがあり、これはメッセージの送信元のブラウジングコンテキストに対応するWindowオブジェクトが入っています。つまり、これを使えばメッセージを送ってきたページに対してメッセージを送り返すことができます。例えば返事を送り返す場合はこうですね。


window.addEventListener("message",function(e){
  e.source.postMessage("返信", e.origin);
});

ではここで、ここまでのをまとめたサンプルを用意しました。ソースには簡単な解説を用意してあるあるので参考にして下さい。

次回はWeb Messagingの応用を紹介します。