DOMおれおれAdvent Calendar 2015 – 14日目分
TreeWalkerというオブジェクトがあります。その名の通り、(DOMの)ツリーを歩き回るオブジェクトです。
例えばこんな構成。(インデント等によるテキストノードはないものとします。)
div#root
+ div#el-1
+ div#el-1-1
+ div#el-1-1-1
あるノード(#root
)を起点に、長子の子孫を巡り(#el-1
→#el-1-1
→#el-1-1-1
)、また戻ってくる(#el-1-1
→#el-1
→#root
)という処理が必要だとします。
普通に関連ノードを参照して移動していくコードはこんな感じです。
var origin = document.querySelector('#root'); var current = origin; // 子孫を巡る while (current.firstChild) { current = current.firstChild; console.log(current.id); } // 祖先を巡る while (current.parent && current !== origin) { current = current.parent; console.log(current.id); }
何も問題ないように見えますか? 実はこれ、はい、何も問題ありません。まあ問題はないんですが、せっかくなのでこれをTreeWalkerを使っておきかえるとこんな感じです。
var origin = document.querySelector('#root'); var walker = document.createTreeWalker(origin); // 子孫を巡る while (walker.firstChild()) { console.log(walker.currentNode.id); } // 祖先を巡る while (walker.parentNode()) { console.log(walker.currentNode.id); }
TreeWalkerが指し示すノードは currentNode
に格納されており、この内容がくるくる変わります。 parentNode()
はプロパティではなくメソッドで、 walker
の状態を変更する副作用を持ちます。
ちなみに戻り値に次の currentNode
に相当するものを返します。対象が存在しない場合は null
を返し、 currentNode
の値を変更しません。
全部めぐる
せっかくなので長子だけじゃなくて全部歩き回るコードも載せておきます。
var walker = document.createTreeWalker(document.body); walkThrough(walker); function walkThrough(walker) { if (walker.firstChild()) { do { var node = walker.currentNode; // 要素ノードなら要素名を出力し、子の子を出力 if (node.nodeType === node.ELEMENT_NODE) { console.log(node, node.tagName); walkThrough(walker); } // 文字列ノードなら文字列を出力 else if (node.nodeType === node.TEXT_NODE) { var text = String.trim(node.nodeValue); if (text) { console.log('#', text.slice(0, 127)); } } } while(walker.nextSibling()); walker.parentNode(); } }
その他
document.createTreeWalker()
で作ります。ルートになるノードの指定が必須です。- 第二引数でフィルターを指定する事ができます。
- 例:
document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT)
- フィルターは
|
で区切って複数指定できます。
- 例:
- ルートとされたノードは
walker.root
に格納されます。 - ルートとされたノードより上位へはたどれません。
walker.parentNode()
がnull
を返します。
使い道
んー特に思い付きませんでした。
同じインスタンスを使い続けられるので、参照の管理が楽なのが利点?
環境
IE 9+。その他問題ないようです。