2007年11月17日 04:00 [Edit]

javascript - DOM時代のdocument.write()

id:amachang++;

[JavaScript] とてもシンプルに自分自身が属する script 要素を取得 - IT戦記
こうすることで、currentScript はこの script 要素を指す。

これにより、document.write()をこう書き直せます。

document.getCurrentScript = function(){
  return (function (e) {
    if (e.nodeName.toLowerCase() == 'script') return e;
    return arguments.callee(e.lastChild)
  })(document)
};
document.write = function (what, where) {
  if (!where) where = this.getCurrentScript();
  var here = (function(node, cls){
    var kids = node.childNodes;
    for (var i = 0, l = kids.length; i < l; i++){
      var kid = kids[i];
      if (kid.nodeType != 1) continue;
      if (kid.getAttribute('class') == cls) return kid;
      // if (kid.hasChildNodes()) return arguments.callee(kid, cls);
    }
    return null;
  })(where.parentNode, '.written');
  // var here = where.previousSibling; // this does not work!
  if (!here){
    here = document.createElement('div');
    here.setAttribute('class', '.written');
    where.parentNode.insertBefore(here, where);
  }
  if (what.nodeType){
    here.appendChild(what);
  }else{
    here.innerHTML += what;
  }
};

以下、きちんと動いている証拠。FireFox, Safari, Operaで動作確認してあります。IEの報告きぼんぬ。

見ての通り、文字列だけではなくDOM Elementも書き出し可能で、組み込みのdocument.write()とは上位互換になっているはずです。

考え方としては、以下と同様なのですが、

空繰再繰 - document.writeをDOM仕様にする
こんな感じ。
var currentScript = (function (e) {
    if(e.nodeName.toLowerCase() == 'script') return e;
    return arguments.callee(e.lastChild)
})(document);

document.write = function ( node, referenceNode ) {
    if ( typeof(referenceNode) == 'undefined' ) {
        referenceNode = currentScript;
    }
    referenceNode.parentNode.insertBefore( node, referenceNode.nextSibling );
}

それだと既存のdocument.write()と非互換になってしまうので。

実装上の留意点として、こちらでは<script>タグの直下ではなく直上に<div class=".written">なnodeを作ってそこに書き出しています。その方がわずかですが手間が少ないので。

もう一つ、これを書いている間に面白いことを見つけました。node.insertBefore()しても、node.previousSiblingnode.nextSiblingはscriptの実行が完了し、DOM Treeが再描画されるまで更新されないのです。わざわざ<div class=".written">なnodeを検索しているのはそのためです。

Enjoy!

Dan the JavaScripter

追記:うーん、AMNの広告はこれだと非表示になってしまうなあ。というわけで対策として本entryの最後でdocument.write()を組み込みのそれに戻しています。


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

この記事へのトラックバック
というわけでさらに添削。 404 Blog Not Found:javascript - 添削 - DOM時代のdocument.write() 追々記: それでも、一時的に戻すというのがやはり気に食わない。別の実装を考えよう....
javascript - 決定版 - DOM時代のdocument.write()【404 Blog Not Found】at 2007年11月17日 22:14
その他不具合を 404 Blog Not Found:javascript - DOM時代のdocument.write()これだと、同じ親の下に複数のdocument.writeがあっても、最初の所に全部書き出されてしまいます。
javascript - 添削 - DOM時代のdocument.write()【404 Blog Not Found】at 2007年11月17日 14:33
この記事へのコメント
IE だと動きません、というか、このページを読み込もうとするとエラーになります。
Posted by まじかんと at 2007年11月17日 12:33
不完全ですがdocument.write(ln)?をapplication/xhtml+xmlな
ページで動かすためのスクリプトを書いてたりします。
参考までに。

http://nyarla.net/blog/javascript-tips6
Posted by にゃるら、 at 2007年11月17日 10:27
これだと、同じ親の下に複数のdocument.writeがあっても、最初の所に全部書き出されてしまいます。
http://zoriolab.info/sample/documentwrite.html
Posted by zorio at 2007年11月17日 08:04