2012/03/19

jQueryのload()とdocument.write()の共存

個人情報さらしてみる。
このページを管理している(現時点)のは俺なんだが、jQueryを使い始めた当時は何故か、真っ白なページに
Powered by FC2.com
とだけ表示されていて、大いに困った。いえでもちょろめでも火狐でもこうなる。
火狐に至っては「wyciwyg://103//http://~~」とかわけのわからんパスに飛ばされる始末。
で、最終的になんとかこのトラブルを(とりあえず)解決したのでここに記録しておく。

ずっと「読み込み中」だからFirebugが使えない、しかもwyciwyg://って…

ローカルの鯖(Apache)では正しく動作していたのに、リモートでは動かない。
プログラミングではよくあることだと自分をなだめつつ調査開始。

まず、一瞬あるべき姿のページが表示されたのち、ページがリセットされて「Powered by FC2」の一行のみになる。
アドレスバーを見るも、以上は無さそうだが、なぜかFirebugとかがページを移動したときと同じ挙動をする。
履歴を見てみると、なんと「wyciwyg://##/http://~~~」となっていて、##の部分は毎回数字が変わる。
そこで「"wyciwyg://"」をggr。すると「wyciwyg://」ドキュメントへの不正アクセスというのが引っかかった。
(ついでに、「WYCIWYG」は"What You Cache Is What You Get"らしい。(→Wikipedia))
でもこれMozillaってことは、Firefoxじゃん。しかも
「修正済みのバージョン Firefox 2.0.0.5」
ふ・ざ・け・る・な!

「Powered by FC2」ェ…

これは手がかりにならないので、ChromeとIEで見てみると、やっぱりFirefoxと同じ症状(ただしwicywigではないが)。
仕方ないのでfc2鯖との相違点を探す。てか一行広告しかないじゃん。
てことで一行広告を調べる。
どうやらbody要素の最後に次の内容が入るようだ。

<script type="text/javascript"><!-- var fhp_c_pc = navigator.userAgent.toLowerCase(); var fhp_ie = ((fhp_c_pc.indexOf("msie") != -1) && (fhp_c_pc.indexOf("opera") == -1)); var fhp_cs, fhp_wt, fhp_dm; if (fhp_ie) { fhp_cs = document.charset; }else{ fhp_cs = document.characterSet; } fhp_dm = encodeURI(document.location); fhp_wt = ""; fhp_wt = '<' + 'script src="http://web.fc2.com/header.php?cs=' + fhp_cs + '&dm=' + fhp_dm + '" charset="UTF-8"><' + '/script>'; fhp_wt += '<' + 'script src="http://web.fc2.com/footer/footer.php?cs=' + fhp_cs + '&dm=' + fhp_dm + '"><' + '/script>'; document.write(fhp_wt); //--></script> <!-- FC2, inc.--> <img src="http://media.fc2.com/counter_img.php?id=50" style="visibility:hidden" alt="inserted by FC2 system" width="0" height="0" /> <!-- FC2, inc.-->

なんで直接入れないんだよ… これがさらに展開されて、document.write()で追記される。

http://web.fc2.com/header.php
fhp_head='';
web.fc2.com/footer/footer.php
//http://kesc.web.fc2.com/ document.write('<div id=\"fc2_footer\">'+"\n"); document.write('<span style=\"font-size:12px;\">'+"\n"); document.write('<br>'+"\n"); document.write('Powered by <a target=\"_blank\" href=\"http://fc2.com/\">FC2.com</a>'+"\n"); document.write('</span>'+"\n"); document.write('</div>'+"\n"); document.write(''+"\n"); move_fc2_footer(); function move_fc2_footer(){ if(!document.getElementById("fc2_footer")){return;}; var ff=document.getElementById("fc2_footer"); ff.style.textAlign="center"; ff.style.width="100%"; ff.style.lineHeight="14px"; }

開始タグと終了タグが別々に…
こういうのは処理系を開発する側としては辛いんだけどな…
というか、DOMがどうとか言ってるこのご時勢に、document.write()とかアリなのか?

そもそもdocument.write()がだな

再びggったところ、どうやらdocument.write()自体は問題ないらしい。
仕方ないので、htmlに仕込んだスクリプトをコメントアウトしながら原因調査。すると…
$(~).load()を入れるとdocument.write()のタイミングでおかしくなることが発覚。
やっぱAjaxとは相性悪いんだな…

しかし大人の社会とやらは

しかし、「相性が悪い」という理由で広告を消してしまうと契約違反なので、がんばってdocument.write()を動かさなければならない。
どうするか。
document.writeをオーバーライドするしかない。
普通に考えれば、

  1. document.write()の引数の文字列をためていく
  2. すべて読み終わったらタグ生成&表示
となりそうだが、ここで問題が。
document.write()で出力したscriptタグの内容って、もう一度document.write()を呼ぶんじゃね?
こりゃ参った。正攻法でなんとかしようと思ったら、かなり面倒になる。

というわけで、手抜きしました。

document.write()で開始と終了タグを別々に出力するとはいっても、たかが何ミリ秒のレベルだろ?
というわけで、

  1. はじめの書き込みから1000ms間は溜める
  2. 出力。以降は、2000msごとに出力
というかんじのスクリプトを組んだ。
iframeとかで読んだ別のページでもdocument.write()されても親の方に表示されてしまったりするが、現状では困ってないので無視。

jquery_use.js
var __tl_internal_document_write={cntnow:5,str:[],ready:false, add:function(str){ __tl_internal_document_write.str[__tl_internal_document_write.cntnow]+=str; }, show:function(){ if(__tl_internal_document_write.cntnow>0){ $(__tl_internal_document_write.str[__tl_internal_document_write.cntnow]).appendTo($(document.body)); __tl_internal_document_write.str[__tl_internal_document_write.cntnow--]=""; setTimeout(__tl_internal_document_write.show,2000); }else{ __tl_internal_document_write.cntnow=4; } }, fn:function(str){ __tl_internal_document_write.add(str); if(__tl_internal_document_write.ready){ setTimeout(__tl_internal_document_write.show,1000); } } }; // __tl_internal_document_write document.write=__tl_internal_document_write.fn; $(function(){ __tl_internal_document_write.ready=true; __tl_internal_document_write.show(""); });