window オブジェクトを汚さないネタ
user.js や bookmarklet を書く時は、元のページのグローバル関数や変数を上書きしないようにグローバルな領域を使わないようにするんですが、JSONP のような外部スクリプトと連携する場合はどうしても必要になります。それをどうにかして元のページの window オブジェクトを汚さないようにしようとして思いついたネタ。iframe を使います。
手っ取り早くソースを見ていただくとこんな感じ。
function testJSONP(){ var url='http://b.hatena.ne.jp/entry/json/?url=http%3A%2F%2Fwww.hatena.ne.jp%2F&callback=callback'; var iframe=document.createElement('iframe'); iframe.style.display='none'; document.body.appendChild(iframe); // appendした後でないとcontentWindowが作られない var iWin=iframe.contentWindow; var iDoc=iWin.document; iDoc.write('<html>'); // 一度 write した後じゃないと消えちゃう var thisDocument=document; iWin.callback=function(data){ setTimeout(function(){ // setTimeoutが無いとOperaで問題(詳細略) if(data)alert(data.title); //thisDocument.body.removeChild(iframe); document.body.removeChild(iframe); // なんでこれで動くの? },0); }; iDoc.write('<head>'); iDoc.write('<script type="text\/javascript" src="'+url+'"><\/script>'); iDoc.write('<\/head>'); iDoc.write('<\/html>'); iDoc.close(); // Opera でエラーが起こる(けど無視) }
(汎用的な Tips にしたかったのにバッドノウハウの詰め合わせみたいになってしまった。)
デモ(すぐ消します)
詳細は省きますが、要するに元のページのグローバル領域は汚したくないから iframe で新しいグローバル領域を作ってそいつを汚しまくるというもの。考え方は「最速インターフェース研究会 :: prototype.jsのObject汚染を回避する方法」と同じです。
ソースのコメントに書いているように、よくわからないのは「callback の中の document(windowも同じ) は iframe の document じゃないのか」ということです。function 宣言時の document がクロージャ的に参照されるのかな? うーん。
で、これを応用するとライブラリの動的ロードなんかも書けます。
iDoc.write('<head>'); iDoc.write('<script type="text\/javascript" src="prototype.js"><\/script>'); iDoc.write('<\/head>'); iDoc.write('<body onload="callback(window)"><\/body>'); iDoc.write('<\/html>');
もちろんライブラリは iframe 側でロードされますから元のページでそのまま使うことは出来ません。callback の中でコピーするなり iframe 越しに実行したりする必要があります。
ライブラリを iframe を使ってロードすると、ライブラリ間の名前の競合を全く気にしなくてもいいというメリットがあります*1。なので今後ライブラリを書く時は、こういった利用法を想定して、特定の window オブジェクトに依存しない書き方をしようと思いました。
*1:むしろ1ライブラリ1インラインフレームでもいいんじゃないかと思います