風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

IFRAMEの高さを内容に応じて変えるには?(クロスドメインで)

瞬語一覧をブログパーツ化しようとして、はまる

先日の瞬語一覧を、とりあえずお手軽にGoogle Gadget化してみるか……と安直に考えて、はまってしまいました。

瞬語一覧 - “今”話題のキーワードって?は、タグクラウドっぽい表示にしている性質上、コンテンツの高さが変わってしまうことは避けられません。

で、調べてみたら、Google Gadgetにはdynamic-height Libraryの中に、_IG_AdjustIFrameHeight()という関数が用意されており、ガジェットの高さを調整してくれるみたいです。

なんだ、簡単じゃんと思ってやってみたのですが……。

試してみたい方は→こちら

_IG_AdjustIFrameHeight()の問題点

  1. サイズ調整動作が鈍い(IFRAMEの内容が読込まれてから高さ調整までに一拍置かれる感じ)。
  2. 動作が不安定(サイズ調整してくれないときがある。特にIEやOperaで顕著)。
  3. Google Gadgetのプレビュー画面などではサイズ調整してくれない。

そもそも、IFRAMEの高さを調整しようとすると……

  1. 親ウィンドウ(便宜上Wpとする)のドキュメント(Dp)下にあるIFRAME(Fp)により子ウィンドウ(Wc)にドキュメント(Dc)を読込む。
  2. Dcの読込み(及び必要な処理)が完了した時点で、Dc上の要素の高さ(Hc)を取得する。
  3. 親ウィンドウ(Wp)上のIFRAME(Fp)の高さ(Hp)に、Hcの結果を反映する。

ところが、親(Dp)と子(Dc)のドメインが異なっていると、セキュリティ上の制約により、親が子(Dc)上の要素をのぞき見したり、子が親(Dp)の要素を変更したりすることができません(=Fpの高さ(Hp)が変更出来ない)。

クロスドメインでの回避策

Google Gadgetの回避策

上記セキュリティ上の制約を回避するため、Google Gadgetでは、

  1. 子ドキュメント(Dc)上に隠しIFRAME(Fc)を作成し、この下の孫ウィンドウ(Wgc)上に、親ドキュメント(Dp)と同一ドメイン上の孫ドキュメント(Dgc)を読込む。
  2. 親(Dp)←→孫(Dgc)間の通信を行う親(Dp)が孫ウィンドウ(Wgc)のlocation.hrefを定期的に監視(DgcのURL(=Fcのsrc=Wgcのlocation.href)の#以下にパラメータを指定してやりとり)。

ようにしている、らしいです*1
_IG_AdjustIFrameHeight()の動作がイマイチ不安定なのも、この辺りの通信方式に少々無理があるのでは、と想像しています。

といっても

Google グループ

を斜め読みしただけで、詳細は調べていませんが……。


■追記(2009/06/07)
親ドキュメント(Dp)下のIFRAME(Fp)には"iframe_xxx"(xxxはガジェット毎に独立)、子ドキュメント(Dc)下の隠しIFRAME(Fc)には"iframe_xxx_n"(nは数値、通常は0?)という名前がついており、
Wp.frames["iframe_xxx"].frames["iframe_xxx_n"].location.href
を定期的に監視して、その#以下をパラメータとして参照しているみたいですね。
ちなみに孫を同一ドメインにするためのダミーURLとして"http://設置したサイトのドメイン/robots.txt#パラメータ"を指定しているのですが、存在しないサイトもあるだろうし(実際はてなダイアリーでは404)、確実な動作が望めるかというと疑問符が付きそうな手法ですね。


思いついた回避策

要は、親(Dp)と子(Dc)のドメインが異なっているのが問題なので、どうにかして同一ドメインにしてやればよいのでは……ということで、

  1. 子ドキュメントの内容(テキスト:Tc)を予めクロスドメイン取得(サーバ経由で、たとえばGAE_xmlhttpRequestのようなものを使用する)。
  2. 親ドキュメント(Dp)下にIFRAME(Fc)作成し、Tcの内容を反映して子ドキュメント(Dc)とする(srcは指定せず、Wc(=Fc.contentWindow).document.write(Tc))。このとき、パラメータ(親のIFRAME(Fp)のid等)はWc.nameで指定してやる。
  3. 子ドキュメント(Dc)の処理が完了したら、自身の高さ(Hc)を取得し、親ドキュメント(Dc)上のIFRAME(Fp)の高さ(Hp)に反映。

のようにしてみました。
これで、ブログパーツ提供元(furyu-tei.sakura.ne.jp)とは異なる任意のドメイン(下記例では、furyu.tea-nifty.com)上ページにも、IFRAMEの高さを自動調節可能なブログパーツが設置できるようになります。

瞬語一覧をブログパーツ化してみた: 風柳亭

もっとも、はてなダイアリー上には設置できませんが(苦笑)。

注意点

上記回避策(あらかじめテキストを取得して子ドキュメントに書込んでやる方)ですが、document.write()した場合、SCRIPTタグでsrc指定してやったスクリプトが、読込み完了を待たずして次の処理に移ってしまう(従って、SCRIPTタグで指定したスクリプト内の関数を使う処理は読込み完了を待ってから走らせる必要がある)など、通常のページ読込みとは異なる動作をするようですので、注意が必要になります。
あと、Firefox3で気がつきましたが、あらかじめIFRAME要素を作っておき、非同期のイベント完了時にwrite()する……という手順でやるとはまります(write()した内容が画面上に表示されない。IE/Chrome/Opera/Safariは問題無く表示される)。
IFRAME要素の作成自身も非同期イベント完了時(write()の直前)に行うようにすれば表示されました。

*1:実際、隠しフレームが作られて、srcに"http://d.hatena.ne.jp/robots.txt#resize_iframe%26remote_iframe_0%26264$"みたいな感じでセットされている模様。264が高さ、かな……って、robots.txt固定?