DOM トラバーサル

本記事は、マイクロソフト本社の IE チームのブログから記事を抜粋し、翻訳したものです。
なお、本記事中の Internet Explorer 9 は、元記事執筆当時の ( 正式リリース以前の) Platform Preview  についてのものになりますのでご了承ください。

【元記事】DOM Traversal (2010/7/30)

最新の Platform Preview ビルドには、DOM の操作に役立つ相互運用性を備えた機能として DOM トラバーサル (英語)要素トラバーサル (英語) の 2 つが用意されています。この 2 つの機能を使えば、Web 開発者はブラウザーの種類を問わず同一のマークアップを使ってドキュメントを簡単かつ柔軟、高速にトラバースできます。どちらの機能もフラットな列挙処理を行います。DOM ツリーを反復処理可能なリストに単純化し、トラバースする対象のノード群をフィルタリングによって絞り込むことができます。両機能とも、ブラウザーの種類を問わず同一のマークアップで機能するため、この記事で使用するコードは IE9 Platform Preview およびその他の各種ブラウザーでお試しいただけます。

ページ内のある要素を探したい場合、この 2 つの機能がなければ、firstChild と nextSibling を使って深さ優先の縦型のトラバース処理を繰り返さなければならず、コードが複雑になり、実行速度も低下します。DOM トラバーサルや要素トラバーサルを利用すれば、新しい方法でより効果的に問題を解決できます。このブログ記事では、これらの概要といくつかのベスト プラクティスを紹介します。

最初に要素トラバーサルについて説明しましょう。要素トラバーサルは、最も単純なインターフェイスで、DOM 要素の列挙によく使われるパターンを使用しています。要素トラバーサルは、基本的には、DOM Core を要素用に最適化したものです。firstChild と nextSibling の代わりに、firstElementChild と nextElementSibling を呼び出します。以下はその例です。

 if (elm.firstElementChild)
{
    elm = elm.firstElementChild;
    
    while (elm.nextElementSibling)
    {
        // Do work...
    }
}

この方法は高速で便利です。テキストやコメントのノードを無視して、要素だけを対象に処理を行うことができます。

DOM トラバーサルは、より多様な用途を考慮して作られています。次のように、最初に反復メソッドの NodeIterator または TreeWalker を作成し、そのいずれかを使ってツリーをトラバースします。

 var iter = document.createNodeIterator(elm, NodeFilter.SHOW_ELEMENT, null, false); // This would work fine with createTreeWalker, as well

var node = iter.nextNode();
while (node = iter.nextNode())
{
    node.style.display = "none";
}

このコードは、ツリー内のすべてのノードをフラットなリストとして反復処理します。ノードが他のノードの子なのか兄弟なのかということより、ドキュメント内の現在位置より前なのか後ろなのかということの方が重要な場合は多くありますが、そうした場合にこのコードはとても役に立ちます。

DOM トラバーサルの大きな利点は、フィルタリングの概念が導入され、必要なノードだけをトラバースできるようになったことです。NodeIterator はフラットな反復を実行するだけなのに対して、TreeWalker には、firstChild() などのメソッドが用意されており、指定のレベルでツリー構造の中を調べることができます。

SHOW_* という定数 (上の例では SHOW_ELEMENT) を使うと、テキストや要素などさまざまな種類のノードを指定できます。この定数を使用することで十分な場合がほとんどですが、より細かい制御が必要な場合は、NodeFilter インターフェイスを介して独自のフィルターを作成することができます。次の例に示すように、NodeFilter インターフェイスはコールバック関数を使用して各ノードをフィルタリングします。

 

 var iter = document.createNodeIterator(elm, NodeFilter.SHOW_ALL, keywordFilter, false);

function keywordFilter(node)
{
   var altStr = node.getAttribute('alt').toLowerCase();
   
   if (altStr.indexOf("flight") != -1 || altStr.indexOf("space") != -1)
      return NodeFilter.FILTER_ACCEPT;
   else
      return NodeFilter.FILTER_REJECT;                
}

実際の動作については、私が作成した DOM トラバーサルのデモ (英語) をご覧ください。このデモでは NodeFilter を多用しています。メディア要素のリストに対する複雑なフィルター操作も、上の例のように NodeFilter コールバックを使用して簡単に実行できます。

この記事では、ドキュメントをトラバースする方法をいくつか紹介しました。各種インターフェイスを使用する際のベスト プラクティスは以下のとおりです。

  • ドキュメントの構造が重要で、要素に対してのみ処理を行いたい場合は、要素トラバーサルの使用を検討します。要素トラバーサルは高速で、コードも短くて済みます。
  • ドキュメントの構造が重要でない場合は、TreeWalker ではなく NodeIterator を使用します。NodeIterator を使えば、単純にフラット リストを処理しているということがコード上明確になります。また、NodeIterator の方が処理が高速になる傾向があります。速度は、大量のノードのトラバースでは重要なポイントです。
  • フィルタリングの要件を SHOW_* 定数で満たせる場合は、この定数を使用します。定数を使用すればコードが単純になり、パフォーマンスも若干向上します。ただし、より詳細なフィルタリングが必要な場合は、NodeFilter コールバックが必要となります。

私も自分でコーディングしてみて、これらの機能がたいへん有効であることを実感しました。これらの機能が今後どのように使われていくのか楽しみです。最新の Platform Preview (英語) をダウンロードして、API をぜひお試しください。また、その感想もお寄せください。

ありがとうございました。
Jonathan Seitel
プログラム マネージャー