DOMのDocumentFragmentですが、今まで使おうと思ったことがありませんでした。使わなくても特に不自由しなかったからです。しかし、これを使わないと洒落にならない事例に出くわしました。
なんだか長くなったので別の文書にしました。
JavaScript有効かつDOM1 HTML対応なブラウザなら処理速度を体験できるようになっています。次の実装チェックボタンを押しても何も起らなかったり、falseとアラートされた場合、体験できません。
innerHTMLを利用した場合と比較していますが、innerHTMLはMSの独自拡張です。対応していない場合はエラーになります。ご了承ください。
私が認識しているDocumentFragmentの主な特徴は、以下の通りです。
たとえば,DocumentFragment がもってよい子は1つだけであり,その子ノードは Text ノードであってもよい。
DOM1(コア) より
これは誤読される可能性が高いのではないでしょうか。DocumentFragmentは幾つでも子を持てます。そしてそれが重要な特徴です。原文はこちら:
For example, a DocumentFragment might have only one child and that child node could be a Text node.
Document Object Model (Core) Level 1 より
これは、DocumentFragmentの子が整形式のXMLでなくても良いという文脈で語られています。その例というわけです。私なら次のように訳します。
that child nodeを「それ」と訳しましたが、「その子ノード」と直訳すると、「その」が何なのか不明瞭になります。
この一年間、糞HTMLには悩まされ続けてきましたが、今回が一番嵌りました。次の糞HTMLの糞フラグメントをご覧下さい。
<p id="parent">
<b id="me">
<ul id="child">
<li>list item</li>
</ul>
</b>
</p>
さて、この例のb要素(HTML B Element)へのポインタを幾つか作成したいと思います。親からアクセスするか、子からアクセスするかというニ通りの経路を考えます。
var elmBviaParent = undefined;
var nlBSiblings = document.getElementById('parent').childNodes;
for(var i=0; i<nlBSiblings.length; i++)
{
if(nlBSiblings.item(i).id == 'me')
elmBviaParent = nlBSiblings.item(i);
}
var elmBviaChild = document.getElementById('child').parentNode
この2つの変数が共に同一のb要素へのポインタになっている事を確かめる為、まずid属性値を比較してみます。
elmBviaParent.id === elmBviaChild.id
これは真です。共に「me」という文字列になっています。Internet Explorer6は、b要素の子としてul要素を持てるということでしょうか。
しかし、ここが怪しい部分です。そのまま比較してみましょう。
elmBviaParent == elmBviaChild
これも真です。まあ私は勝手に型変換されるのが恐いので、その必要がない限りは===で比較するようにしています。すると:
elmBviaParent === elmBviaChild
さあこれは偽です。falseです。両者がポイントしているのは違うオブジェクトであることが判明しました。==演算子で比較した場合は、型変換を通じて真だったのです。
両者の内のどちらかは、DOM1 HTMLのElement型では無いということでしょうか。これだからJScriptの==演算子は恐いのです。String型とNumber型以外ではどういう型変換が行われるのかはっきりしたことが分からないのです。特に恐いのは、効率の問題です。任意の2つのオブジェクトを比較した時、コード作成者には未知の数の型変換がトライされるのではないかと思うと、とてもじゃあないですが使う気になれません。因みにこれと似たような理由で、最近はwith文を使うのを止めました。
それはさておき、どちらが「なんちゃってElement型」なのかを確認する為、お馴染みの方法でもう一つのポインタを作成してみます。getElementByIdメソッドです。
var elmB = document.getElementById('me')
1. 、2. 両者を、このelmBと比較してみます。
elmBviaParent === elmB
elmBviaChild === elmB
2-1. が偽です。b要素の子要素のul要素からparentNodeであるb要素を参照した場合には、Element型ではない何か未知の型「なんちゃってElement型」になっていると想像します。ではこの「なんちゃってElement型」は、DOM1 CoreのElementインターフェイスを実装しているのかといえば、しているようです。MS独自拡張のオマケの部分も含め、アトリビュートもメソッドも、全部実装しています。
そういうわけで、私はString、Number以外のオブジェクトを==演算子で比較したくないんです。何が行われているか良く分からないからです。では上の1. と 2. を比較して、型は兎も角同一の「要素」を参照しているかどうかを調べるにはどうすれば良いでしょうか。
Internet Explorerは、HTML文書の各要素にその登場順の番号を割り当てています。一意の識別子といえるでしょう。これはsourceIndexプロパティで参照できます。
elmBviaChild.sourceIndex === elmBviaParent.sourceIndex
しかし、b要素の子にul要素を作るのはHTMLの文法違反であり、文法を守っていればこの問題は起りません。従って、責任はInternet Explorer側にはありません。
DOM弄りを楽しんでいます。過去の文書操作系のカスタムパネルは、DOM Inspectorをリッチにする形で統一しようと考えています(DOMといってもDOM1 HTML)。その他、危険な香りのするバグを発見したりしました。
ツリーの構築は:
という形で行っています。一気にツリー全てを構築しようとすると、凄まじく時間がかかるからです。最初、これは私のJScriptコードに問題があるのかと思いましたが、何十回と繰り返されるappendChildメソッドによる描画速度の問題のようです。
しかしDOMツリーをカスタムパネル内に視覚化できたとしても、文書内のある要素が、DOMツリー上のどの位置にあるのかが分からなければ、これは完全に遊びに終ってしまうでしょう。fub_netの素晴らしい点は、アクティブタブ内に表示している文書のイベントを、カスタムパネル側で処理できることです。この特徴を利用すれば、「要素をクリックする」等のアクションを介して、DOMツリー上のその要素の位置をスタイルの変更等によって明示することが可能です。
要するにそこで躓いたわけです。わらい。
Ctrl + 右クリックでDOMツリーを展開し、クリックした要素の位置を明示するようにしました。真っ当なHTML文書であれば今のところ問題なく動作するのですが、b要素の子にul要素があるような「妥当でないHTML文書」の場合、その周辺で訳の分からないエラーが発生します。どうにもこうにも原因が分かりません。
これは実はfub_netのバグではありません。IE6のバグです。簡単なブラウザクラッシャーを見つけました。
for(var i in alert){}
メソッドなら何でも良いのです。厄介なのは、typeof(alert)で"object"という文字列が返ってくることです。はて。"function"を期待していた私が馬鹿だったのでしょうか。
臨時企画。今日と明日の二本立てでお送りします。オチは私にも分かりそうで分かりません。
「とほほのWWW入門」が本になりました! 明日発売、とほほ。CGIはないの?
松風 - 今の言葉 より
とほほ。
……って、何故です? 中身はまだ分からないじゃあないですか。例のW3Cの信者たち
の努力が報われているかも知れません。
売られていませんでした。このためにわざわざ神田で降りるのも面倒だったので二軒くらいの書店しか見ていませんが。
というわけで結局オチはつきません。忘れて下さい。