uhyohyo.net

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

十三章第三回 HTML5 Web Messaging

第十三章の「通信」で次に紹介するのはHTML5 Web Messagingです。これは今までと毛色が違い、HTTP通信はしません。

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

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

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

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

それでは、JavaScriptから他のブラウジングコンテキストを操作するにはどうすればいいのでしょう。ひとつは、JavaScriptから新しいウィンドウを開くことです。最近のブラウザではウィンドウもタブの一種ですので、ここでブラウジングコンテキストが誕生します。

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

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

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

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

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

ボタンを押すと、window.openによって新しいタブが開かれます。このウィンドウには内部的に第二引数の"google"という名前がつけられます。

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

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

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

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

ちなみに、今開いているページのブラウジングコンテキストはwindowという変数に入っています。

ちなみに、windowはdocumentというプロパティを持ち、これは今まで慣れ親しんできたdocumentです。つまり、

window.document === document	//true

ということです。

つまり、window.openの戻り値であるwindowの、documentプロパティを操作すれば、開いたウィンドウの中身を操作できるかもしれませんね。

ところが、今回Googleを開いたように、異なるオリジンの場合はセキュリティの関係から操作できません。それでは同じオリジンなら操作できるのでしょうか。試してみましょう。下に2つのボタンを用意しました。それぞれ以下のような動作です。

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

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

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

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

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

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

メッセージを送る

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

引数は、第一引数が送るメッセージです。送ることができるメッセージは、リテラル(文字列とか数値とか真偽値とか)や、普通の(Objectのインスタンスの)オブジェクトや配列、さらにはDataオブジェクトの他に、最近よく出てくるFile,Blob,FileList,ArrayBufferを送ることができます。

さらに、第二引数は送り先のオリジンです。これは、意図しないサイトに向けて情報を漏らしてしまわないように、送り先のオリジンを指定できるということです。

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

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

のようにします。そうすると他のドメインのサイトには届きません。

この第二引数は省略できません。ですが、例えば自分のサイトの別のページに送ったりすることも多いとおもいますが、そういう場合は省略して"/"を使えます。つまり、"/"を使った場合、送り先は送り元と同じオリジンであると解釈されます。つまり、

postMessage("メッセージ","/");

という感じです。

ほかに、送り先がどのオリジンでもいいという場合は"*"にしますが、なるべく避けたほうがよいでしょう。

ここで注目すべきことは、メッセージを他のオリジンに送ることもできるということです。基本的にJavaScriptからは他のオリジンは操作できませんから、これは珍しい事例であるといえます。

メッセージを受け取る

さて、メッセージを送られたら、送られた側では当然受け取る仕組みが必要になります。ここで使われるのがmessageイベントです。すなわち、あるWindowに対してメッセージが送られてきた場合messageイベントが発生するのです。ここで、messageイベントが発生するのはWindowオブジェクトにおいてです。すなわち、こうです。

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

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

ここで、前回の内容を覚えていますか。EventSourceにおいてサーバーからのメッセージを受け取るのにつかったのもmessageイベントであり、そのイベントオブジェクトはMessageEventでした。実はこれらは本質的に同じものであり、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);
});

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

メッセージの中身

さて、今まで送ったメッセージは全て文字列でした。しかし、postMessageにおいては他のタイプのメッセージを送ることができるのはすでに上で述べた通りです。

具体的には以下のようななものが送れます。

12345	//数値
"hoge"	//文字列
true	//真偽値
["foo","bar","baz"]	//配列
{foo:3, bar:"hoge"}	//普通のオブジェクト
new Date()	//Dateオブジェクト
          

他に、File,Blob,FileList,ArrayBufferなどのデータも送れます。

Web Messagingをうまく利用して2つのウィンドウを提携したりする機会がもしかしたらあるかもしれません。

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