knowledge base

マークアップ/フロントエンドエンジニアのWEB制作における備忘録です。平日はWEB屋、休日は社会人劇団の主宰・劇作家をしています。

クロスドメインでiframeを扱う

以前、iframeについていくつか記事を書かせて頂きましたが、その続編です。

iframeで別ページを読み込む際、同じドメインでないとエラーが発生し、iframe内の要素にアクセスすることができなくなってしまいます。

一例ですが、子ページと同じ大きさにiframeをリサイズすることができなくなってしまいます。 対処法をまとめます。

postMessageを使う

親ページ→子ページへのアクセスは禁じられていますが、window.postMessage()メソッドを用いて子ページ→親ページへメッセージを受け渡すことは可能になっています。

モダンブラウザ、IE8で実装済みです。

子ページ側(メッセージを送る側)

parent.postMessage('hello','*');

第一引数は送りたいメッセージ、第二引数には対象ドメインを指定します。

ここでは対象ドメインに「*(すべて可)」を指定していますが、本来はセキュリティ上、親ページのドメインを指定したほうがよいそうです。

ちなみに第一引数のメッセージですが、IE8では文字列のみ、モダンブラウザでは文字列およびオブジェクトが送信可能です。

ではIE8でもオブジェクトを送りたい場合(プロパティと値を結び付けたい場合)は、どうすればよいのか。

JSONオブジェクトを文字列化して送ります。

var size = {'width':100};
size = JSON.stringify(size);
parent.postMessage(size,'*');

これを、ページが読み込まれた際に実行するように紐づけます。 

親ページ側(メッセージを受ける側)

 windowオブジェクトのmessageイベントで、送られたメッセージを受け取ることができます。

送られたメッセージは、次のようにして取得できます。

値の格納場所が異なるので注意。

/*jQueryの場合*/
$(window).on('message',function(e){
  var size = JSON.parse(e.originalEvent.data);
  var _width = size.width;
});

/*ネイティブなJavaScriptの場合*/
window.onmessage = function(e){
  var size = JSON.parse(e.data);
  var _width = size.width;
};

子ページで文字列化されたオブジェクトを、JSONオブジェクトに戻すところが重要です。圧縮→解凍のようなイメージでしょうか。

親ページのハッシュを利用する

以上はIE8までで実装可能な方法ですが、IE7以下でも異なるドメイン間でiframeを扱うにはどうすればよいでしょうか。

親ページのハッシュを利用するという、裏ワザを用います。

渡したい値を親ページのハッシュに与えます。

ハッシュならば親ページのリロードが発生しないためです。

子ページ側(メッセージを送る側)

parent.location.hash = 'width=' + width;

親ページはparentオブジェクトで参照可能です。

対する親ページは、ハッシュを常に監視し、ハッシュが変化したらその値を取得するという方法をとります。

setTimeoutで断続的に関数を実行します。

親ページ側(メッセージを受ける側)

var locationHash = location.hash;
var resizeByHash = function(){
  var _hash = location.hash;
  var timer = setTimeout(function(){
    if(_hash != locationHash){
      clearTimeout(timer);	
      /*ハッシュに仕込まれた値を取り出す処理*/
    }else{
      resizeByHash();
    }
  },50);
};
resizeByHash();

親ページのリサイズに合わせてメッセージを送りたい

たとえば親ページのリサイズに合わせて、iframeを拡縮する際に効果を発揮します。

先ほどparentオブジェクトで親ページを参照できたことを利用します。

子ページ側

parent.onresize = function(){
  /*postMessageを送る処理*/
}

親ページ側は、いちどonmessageに紐づく処理を書けば、すべてそこで受け取ってくれます。

注意

しかしながら、postMessageは同一ドメイン間で安全にメッセージングをするためのものなので、クロスドメインといってもhttpとhttpsとの間ではエラーが起こります

クロスドメイン間の通信は、JSONPのほうが無難なのかもしれません。

また、postMessageを使用できるブラウザ判別は、Modernizrで調べたほうが無難でしょう。