iframe 関連処理まとめ
iframe 関連の処理で分かったことをこちらにまとめてこうかと思います。コードは jQuery ベースです。
変更履歴
- 2009.06.17
- Safari,Chrome にて動的に生成したiframe内に CSS ファイルを読み込ます方法が分からないとしてましたが、codeなにがし(http://code.nanigac.com/forum/view/488)で教えていただき、記事を修正しました。
iframe 内 を親 html から参照する
同一ドメインであることが前提です。
//iframe 内の window オブジェクトを参照する $('iframe')[0].contentWindow //iframe 内の document オブジェクトを参照する $('iframe')[0].contents()[0] //iframe 内の html 要素を参照する $('iframe').contents().find('html')[0] //iframe 内の head 要素を参照する $('iframe').contents().find('head')[0] //iframe 内の body 要素を参照する $('iframe').contents().find('body')[0] //iframe 内の div 要素を参照する $('iframe').contents().find('div')[0]
iframe 内の script を実行する
子 html
function setBlue(){document.body.style.backgroundColor='blue'}
親 html
$('iframe')[0].contentWindow.setBlue()
iframe の中身 を親 html から書き換える
動的に生成した iframe の中身 を書き換える
//iframe 生成 var ifm = $('body').append('<iframe/>').find('> :last-child'); var idoc = ifm.contents(); //iframe内ドキュメントの生成 idoc[0].open(); idoc[0].writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"') idoc[0].writeln( '<html>\ <head>\ <\/head>\ <body>\ test\ <\/body>\ <\/html>'); idoc[0].close(); //div要素の追加 idoc.find('body').append('<div>add</div>'); //scriptファイルの読み込み var sc = idoc[0].createElement('script') sc.src = 'test01.js'; idoc.find('head')[0].appendChild(sc) //scriptの実行 var sc = idoc[0].createElement('script') sc.text = 'setTimeout(function(){document.body.style.backgroundColor="red"},1000)'; idoc.find('head')[0].appendChild(sc) //cssファイルの読み込み var cs = idoc[0].createElement('link') cs.href = 'test01.css'; cs.type = 'text/css'; cs.rel = 'stylesheet'; idoc.find('head')[0].appendChild(cs)
head 要素ではなく body 要素に対し appendChild してるのは、Chrome と Safari でエラーになるためです。
また、css ファイルの読み込みについては、Chrome と Safari の場合はエラーにはなりませんが、css 定義内容が適用されません。
クロスブラウザで、css ファイルの読み込みと head 要素への挿入を可能にするには以下のようにするとできます。(動的に生成した iframe に対して実現する方法が分かりません。)
事前に用意してある html(iframe の中身) を親 html から書き換える
//htmlファイル を読み込む var ifm = $('body').append('<iframe src="template.html"/>').find('> :last-child'); //読み込み完了したら内容を変更する ifm.load(function(){ var idoc = $(this).contents(); var ihead = idoc.find('head'); //bodyのinnerHTML書き換え idoc.find('body').html('test'); idoc.find('body').append('<div>add</div>'); //scriptファイルの読み込み var sc = idoc[0].createElement('script') sc.src = 'test01.js'; ihead[0].appendChild(sc) //scriptの実行 var sc = idoc[0].createElement('script') sc.text = 'setTimeout(function(){document.body.style.backgroundColor="red"},1000)'; ihead[0].appendChild(sc) //cssファイルの読み込み var cs = idoc[0].createElement('link') cs.href = 'test.css'; cs.type = 'text/css'; cs.rel = 'stylesheet'; ihead[0].appendChild(cs) });
iframe で Ajax する
iframe で Ajax した場合、以下のようなメリットがあります。
私の場合「サーバサイドの事情で Shift_JIS でしか出力できないけど Ajax を適用したい」というケースが多いので、Ajax はほとんど iframe ベースです。
即席プラグイン
$.fn.iframeAjax = function(callback){ var self = this; self.each(function(idx){ var ready = 0; var o = self.eq(idx); var target = 'iframeAjax'+($.fn.iframeAjax.cnt++); o.attr('target',target); var ifm = $('body').append('<iframe name="'+target+'"/>').find('> :last-child').hide(); return ifm.load(function(){ if((ready || !(jQuery.browser.msie || jQuery.browser.mozilla)) && callback){ callback.apply(this,[$(this).contents()]) } ready = 1; }) }); } $.fn.iframeAjax.cnt = 0;
使用例
<a href="calc.jsp?date=123">iframeAjaxTest</a> <form method="post" action="calc.jsp"> <input name="data"/> <input type="submit" value="send"/> </form>
$('a,form').iframeAjax(function(doc){ $('body').prepend('<div>'+doc.find('#result').html()+'</div>') });
処理の流れ
- a や form 要素のように get や post が発生する要素に対し、target 設定した iframe を生成します。
- get / post が発生すると iframe の load イベントが発火されるので、この中で callback 関数を実行します。
- この時処理結果として iframe 内 の document オブジェクトを callback 関数に渡してます。
- callback 関数では、この document オブジェクトを起点に find メソッド等を使用して、欲しい処理結果(要素)を取得します。
実装上のポイント
callback 関数を実行するため iframe の load イベントを設定してますが、このイベントは IE と Firefox の場合のみ iframe を body 要素に append したタイミングでも動いてしまいます。ですので append のタイミングでは callback 関数を実行しないようにフラグ(ready)にて制御してます。
target 属性の名前がダブらないように下記のように処理してます。
var target = 'iframeAjax'+($.fn.iframeAjax.cnt++);
ただこのようにしても複数の iframe を使用し、それぞれのフレーム内で上記のプラグインを使用すると重複した名前が適用される可能性があります。この場合異なる iframe 間での重複なので問題なさそうな気もしますが、正しく動作してくれません。なので汎用的に使用する場合は、target 名には、タイムスタンプやランダム値などを織り交ぜた方が安心して使えると思います。