window.postMessageを使ってiframeの中のリンクを外のwindowで遷移させる
まとめ
やりたいこと
iframe の中のリンクをクリックしたら元 window で遷移してほしい。
できた
前提としては 元 window の JavaScript も iframe 内の JavaScript も自由にいじれること。
- 元 window で iframe の load が終わったら iframe の window に対して postMessage で自分の origin を伝える
- iframe 内の window で click を拾って url を window.parent.postMessage で元 window に送る
- 元 window は送られた url を location にぶちこむ
できた!
分かったこと
- そもそも iframe って許可されていないとコンテンツの表示もできなくなってた
- X-Frame-Options とか最近はセキュリティ関係のヘッダいろいろあるんだね
- Sinatra はデフォルトセキュア寄りなので何も表示されずに悩んだ
- window オブジェクトとか普段触らないのでアクセス方法分からなくなる
- postMessage って自由に送れるのかと思ったら送信先の origin をちゃんと送る側が分かってないとダメ
- iframe の src まんまみたいなもんじゃんと思ったけど、逆方向の時には工夫が必要
- window.postMessage では object は送れない。より複雑な双方向のやりとりをするには JSON.stringify などを使ってデータ構造を持たせないと厳しそう。
やったこと
ざっくり以下のような感じ。
parent window
<!-- iframe を作る -->
<iframe name="widget" src="http://example.jp"></iframe>
<script type="text/javascript">
/** iframeのコンテンツを読み込み終わったらparentの何か送る(なんでもよい) */
document.getElementsByName('widget')[0].addEventListener('load', function(e) {
window.frames.widget.postMessage('loaded', 'http://example.jp');
});
/** iframe内のクリックされたurlが送られてくるのでlocationを書き換える */
window.addEventListener('message', function(e) {
if ( e.data ) {
location = e.data
}
}, false);
</script>
iframe の中(http://example.jp)で jQuery を使ったとする。
/** parent windowのoriginを保存 */
window.addEventListener('message', function(e) {
widget.target = e.origin;
});
/** リンクをクリックしたらparent windowに教える */
$('a').on('click', function(e) {
window.parent.postMessage($(e.target).attr('href'), widget.target);
});
工夫したところ、悩んだところ
iframe 内の window では parent の origin が分からないのでそのままでは postMessage の際に origin を指定できない。そこで parent 側で iframe の内容の load イベントを待って parent の情報を送ってやる。
iframe の event を拾うには iframe 要素を、iframe 内に message を送るには window オブジェクトを、使い分ける必要があってなんかメンドイ。