innerHTMLでdocument.writeする - その1

前回のエントリの続きです。defer属性指定しただけではdocument.writeしたときに順番が狂う。foo fooなら foo foo barと表示される。ノードツリーが完成した後にdocument.writeすると大変なことになるしなかなか利用しづらい面もあって、以下のようなコード書いてみました。(IE6、Fx2、Opera8で確認)

ソース

<html>
<head>
<script>
/**
 * parent.insertBefore(node, target)は
 * <parent> <target />  </parent>に対して<target>の先頭にnodeを追加していくDOMメソッド。
 * 何回か実行すると以下のようになる。
 * <parent> <node1 /> <node2 /> <target /> </parent>
 */
function write(parent, target)
{
    return function(str){
        parent.insertBefore(document.createTextNode(str), target);
    };
};

function sample()
{
    var div = document.getElementById('div');
    div.innerHTML = 'foo<script id="script">document.write("bar");document.write("foo")<'+'/script>bar';
    var script = document.getElementById('script');
    // オリジナルのdocument.writeを保存
    var original = document.write;
    // document.writeに上記のwriteを代入
    document.write = write(div, script);
    // script.textの内容を実行します。
    eval(script.text);
    // document.writeを元に戻す。
    document.write = original;
}
</script>
</head>
<body onload="sample();">
<div id="div"></div>
</body>
</html>
実行結果
foo bar foo bar

解説

innerHTMLで<script>を挿入したときはスクリプトの評価はされないがDOMノードには追加されています。そんなんで、id属性とかDOMノード列挙で挿入したスクリプトを取得できるので挿入したソースをtextプロパティで取得(OperaではinnerHTMLで取得できないから。textを利用。)それをevalすればスクリプトは実行されます。
ただdocument.writeとかあると順番が狂うのでdocument.writeをハックしてごにょごにょしてあげれば順番通りにTextノードが追加されてオーケー!

とまぁ

evalとかしてますし汎用的じゃなくてかなりナンセンスです。いつかきっと凄腕のハッカさんが凄いコードを書いてくれるはず。XMLHttpRequestで取得したHTMLをそのままinnerHTMLに挿入してそこにあるscriptもついでに実行したい場合には役に立つはずです。