2007年11月18日 15:20 [Edit]

javascript - 決定版 - DOM時代のdocument.write()

というわけでさらに添削。

404 Blog Not Found:javascript - 添削 - DOM時代のdocument.write()
追々記: それでも、一時的に戻すというのがやはり気に食わない。別の実装を考えよう....

追記アリ


例によってFirefox, Safari, Operaで検証。IEの検証よろしく。

/*@cc_on @*/
/* if IE, do nothing (error 8004004, whatever that is) */
/*@if (@_jscript_version > 0)
@else @*/
document.getCurrentScript = function(){
  // fails when document.write nests like
  // document.write('<'+'script'+'>' +'document.write(...)' + '</'+'script'+'>')
  return (function (e) {
    if (!e) return null // gracefully report failure
    if (e.nodeName.toLowerCase() == 'script') return e;
      return arguments.callee(e.lastChild)
    })(document)
};

document.__write__ = document.write;

document.write = function (what, where) {
  if (!where) where = this.getCurrentScript();
  if (!where) return this.__write__(what);
  var here = where.previousSibling;
  if (!here || here.nodeType != 1 || here.className != '.written'){
    here = document.createElement('div');
    here.className =  '.written';
    where.parentNode.insertBefore(here, where);
  }
  if (what.nodeType){
    here.appendChild(what);
  }else{
    if (what.match(/^<script/i)){
      // needed to have your browser reevaluate the script
      this.__write__(what); 
    }
    else{
      here.innerHTML += what;
    }
  }
};
/*@end @*/

テスト

改良版document.write()された箇所は黄色で。

あいうえお
かきくけこ
<style>
.\.written { background-color: #ffffcc }
</style>
あいうえお<br>
<script>
document.write(1.0 + '<br>');
document.write(1.1 + '<br>');
document.write('<'+'script'+'>' + 'document.write(3.0+\'<br>\')' +'</'+'script'+'>')
document.write(4.0 + '<br>');

</script>
かきくけこ<br>
<script>
document.write(2.0 + '<br>');
document.write(2.1 + '<br>');
</script>

解説

問題は、この前提でした。

[JavaScript] とてもシンプルに自分自身が属する script 要素を取得 - IT戦記
つまり、スクリプトが実行されたとき script 要素は今まさに作られたばかりであり、それよりも後ろの要素が存在しない。さらにこの script 要素を含むすべての要素が 今まさに構築中の状態で document から lastChild をたどって行けば必ずたどり着くのである。

ところが、これが成り立たないときがあるのです。それが、document.write('<'+'script'+'>' +'document.write(...)' + '</'+'script'+'>')のように、document.write()が入れ子になっているケース。バナー広告やブログパーツで多用されているアレです。

そういう場合は組み込みのdocument.write()を使うというのが今回の改良点で、そのためにはgetCurrentScript()がきちんと失敗を報告するようにする必要がありました。

今度こそ決定版だと思います。enjoy!

追記:id:Yuichirouさん、コメントありがとうございます。テスト中、わざと見えないように日付を後ろにずらしてやっていたのに見つかっちゃいました:-)

Dan the JavaScripter

追記:

2007-11-18 - つれずれなるままに…
IEだと Element.setAttribute('class', '.written') はHTML要素のclass属性ではなくDOMオブジェクトのclassプロパティに'.written'をセットするので、Element.className = ".written" とした。

この部分のみ、Yuichirouさんのそれから取り入れて修正しました。今度はIE7でも見れるかな?

追々記(2007.11.22):

とりあえずIEでは/*@cc_on*/を使って無効化するようにしました。


この記事へのトラックバックURL

この記事へのトラックバック
参照 404 Blog Not Found:javascript - DOM時代のdocument.write() 404 Blog Not Found:javascript - 添削 - DOM時代のdocument.write() 404 Blog Not Found:javascript - 決定版 - DOM時代のdocument.write() 特徴 script要素の直上にwriteするのはやっぱ上位互換じゃない
id:dankogaiに代わって私が「DOM時代のdocument.write()」を実装してみる【つれずれなるままに…】at 2007年11月18日 13:07
この記事へのコメント
IE6でも開けませんでした(@Sleipnir2.6)。
セキュアモード(JavaScript off)だと開けるみたい。
Posted by Anonymous at 2007年11月19日 12:33
>今度はIE7でも見れるかな?
見れませんね……
ただ、興味深いことに、原因を探ろうとページをローカルにダウンロードして表示してみると、IE7でもうまく行くのです。また、Firefoxでそれを表示すると、「改良版document.write()された箇所」を示す背景色がネット越しよりも多くの箇所で表示されます。
この辺にバグの原因が隠されているのかもしれません。
Posted by Yuichirou at 2007年11月18日 17:00
あのー、この記事、WinIE7で表示できません(汗)
例によってAMNの広告部分で詰まって、
---------------------------
インターネット サイト http://blog.livedoor.jp/dankogai/archives/50952477.html を開けません。

操作は中断されました
---------------------------
とアラートを出し、「OK」をクリックすると「Internet Explorer ではこのページは表示できません」エラーページに移行してしまいます。


P.S.
>テスト中、わざと見えないように日付を後ろにずらしてやっていたのに見つかっちゃいました:-)
テストの時点から「添削」の方にトラックバックされていましたから。
Posted by Yuichirou at 2007年11月17日 23:08
AMNの広告部分でエラーになるバグが再発していますよ? それ以前というか、動作例で「3.0」が出力されていませんね。
Posted by Yuichirou at 2007年11月17日 19:58