IE6で behavior を使わずに DOMNodeInserted をエミュレートしてみる
* { behavior: expression(...) }
とすると、DOMツリーへのノードの追加を検出できるんだけど、behavior はよく使われるので、他のライブラリとバッティングする可能性が高い。
そこで、behavior を使わずに DOMNodeInserted 相当の機能を実装できないか考えてみた。
<html><head><title>emurate DOMNodeInserted event for IE6</title><style> body * { text-underline-position: expression(onDOMNodeInserted(this)); } .box { border: 1px solid red } </style></head><body><script> function onDOMNodeInserted(elm) { setTimeout(function() { done(elm); }, 0); } function done(elm) { if (!elm.style.getExpression("textUnderlinePosition")) { return; } alert(["tagName=", elm.tagName, "expr=", elm.style.getExpression("textUnderlinePosition")].join(" ")); elm.style.removeExpression("textUnderlinePosition"); /* 新しいノードを検出したときの処理 */ } function addNode() { var node = document.createElement("div"); node.className = "box"; document.body.appendChild(node); } </script> <input type="button" value="add node" onclick="addNode()" /> </div></body></html>
説明
- IE独自のCSSプロパティ(text-underline-position) に expressionを設定してある。body要素以下にノードが新しく追加されると、onDOMNodeInserted(this) を呼び出す。this は 追加されたノード を示している。
body * { text-underline-position: expression(onDOMNodeInserted(this)); }
- setTimeoutを挟み、一人時間差でdoneを呼び出す。expression から呼ばれた関数(コンテキスト/スレッド)上で removeExpression() を実行すると、例外「予期しないメソッドの呼び出し、またはプロパティアクセスです」 が発生するのを回避している。
function onDOMNodeInserted(elm) { // elm = 追加されたノード setTimeout(function() { done(elm); }, 0); }
以下のように削除(removeExpression)だけを setTimeout で別スレッドにくくりだす方法もあると思う。
function onDOMNodeInserted(elm) { // elm = 追加されたノード if (!elm.style.getExpression("textUnderlinePosition")) { return; } done(elm); setTimeout(function() { elm.style.removeExpression("textUnderlinePosition"); }, 0); }
- removeExpression("textUnderlinePosition") でexpressionをつぶしたはずなのに、呼び出されることがあるため(一人時間差やってるからかも)、!getExpression("textUnderlinePosition") でガードしている(removeExpression後は、getExpressionがundefeindを返す)。getExpression や removeExpression に指定するCSSプロパティ名は キャメルケースなので、"textUnderlinePosition" となる。
function done(elm) { // elm = 追加されたノード if (!elm.style.getExpression("textUnderlinePosition")) { return; } // ガード alert(["tagName=", elm.tagName, "expr=", elm.style.getExpression("textUnderlinePosition")].join(" ")); elm.style.removeExpression("textUnderlinePosition"); // expression 削除 /* 新しいノードを検出したときの処理 */ }
実行するとこうなる
ページ読み込み直後に以下のダイアログが順番に表示される。ページ読み込み直後は静的なノードがDOMツリーの順番で検出される。
- [tagName= SCRIPT expr= onDOMNodeInserted(this)]
- [tagName= INPUT expr= onDOMNodeInserted(this)]
- [tagName= DIV expr= onDOMNodeInserted(this)]
add node ボタンをクリックしdivノードを追加すると、動的に追加されたノードを検知しダイアログが表示される。
- [tagName= DIV expr= onDOMNodeInserted(this)]
反省会
- * { behavior: expression(...) } と body * { text-underline-position: expression(...) } はあんまり違わないように見えるけど、選択肢の一つとして。
- behavior と比べてかなり重い気がする。う〜む。