この文書「セレクター API」は、W3C の WebApps ワーキンググループ による「Selectors API (W3C Working Draft 14 November 2008)」の日本語訳です。
規範的な文書は原文のみとなっています。この日本語訳は参考情報であり、正式な文書ではないことにご注意ください。また、翻訳において生じた誤りが含まれる可能性があります。
原文が勧告 (Recommendation) ではなく、策定途中の草案 (Working Draft) であることにご注意ください。
原文の最新版 は、この日本語訳が参照した版から更新されている可能性があります。また、この日本語訳自身も更新されている可能性があります。日本語訳の最新版は、W3C 仕様書 日本語訳一覧 から参照することができます。
Copyright © 2006-2007 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
CSS で広く利用されているセレクターは、木構造をもつ要素群から、目的のものを探すための仕組みです [SELECT][CSS21]。セレクター API 仕様は、セレクターを指定して DOM から Element
ノードを取り出すメソッドを定義します。この API は、文書の中から特定の要素に対し DOM 操作を行いたい時、とても有用なものです。このメソッドにより、特定の要素を抽出する処理を、従来の仕組みより簡単に行うことができます。
この章は、この文書の公開時におけるステータスについて説明しています。このため、他の仕様がこの文書を上書きしている可能性があります。W3C による他の出版物およびこの技術レポートの最新版は W3C 技術レポートインデックス (http://www.w3.org/TR/) で探すことができます。
この文書は、"セレクター API" の最終草案 (Last Call Working Draft) です。W3C メンバーや他の団体は、この文書をレビューし、コメントを送ることができます。コメントの送信先は public-webapps@w3.org (公開アーカイブ) で、件名には [selectors-api] を含めてください。なお、コメントの最終期限は 2008 年 12 月 12 日です。
この草案は、Web 開発者やブラウザーベンダーによって評価されることを期待しています。このドラフトは安定しており、レビュー後にすぐ勧告候補 (Candidate Recommendation) へ進むと考えられています。編集段階にあるこの仕様書の最新版は W3C CVS より取得可能です。また、変更点の詳細は CVS サーバー からも所得可能です。
この文書は Web Applications ワーキンググループ により策定されました。ワーキンググループはこの草案を勧告 (Recommendation) とすることを考えています。
ワーキングドラフトとしての仕様書公開は、W3C メンバーによる支持を意味するものではありません。この文書は更新されたり他の文書と置き換えられたり、また破棄される可能性もある草稿段階の仕様書です。策定中ということを明記せずに、この文書を引用することは適当でありません。
この文書は 5 February 2004 W3C Patent Policy の下で活動するグループにより作成されました。W3C は 特許情報の開示に関する公開リスト を関連する団体と共に、その成果物とあわせて管理しています。リストには情報開示に関する説明もありますので、ご参照ください。特許について十分に知識のある人物が、当該仕様に関し Essential Claim(s) が認められると判断した場合は、W3C 特許方針の第 6 章 に従い情報を開示する必要があります。
NodeSelector
インターフェース
このセクションは非規範的です。
この仕様は、セレクターを引数に取り、それに適合する要素を返す二つのメソッドを定義します。このメソッドを利用することにより、従来よりも簡単に Element
ノードに適合させることができるようになります。どのようなことかというと、getElementsByTagName()
で得られた結果にフィルタをかけるのではなく、クエリにより直接“フィルタ”をかけることができるのです。
このセクションは非規範的です。
この例は ECMAScript [ECMA-262] によって書かれています。
HTML 4.01 で、簡単な表を記述してみます。
<table id="score"> <thead> <tr> <th>Test <th>Result <tfoot> <tr> <th>Average <td>82% <tbody> <tr> <td>A <td>87% <tr> <td>B <td>78% <tr> <td>C <td>81% </table>
グラフを描画したいなどの理由から、この表から結果のセル (“Result”) のみを取得したい場合があります。これを実現するにあたり、二つの方法が考えられます。ひとつは、DOM Level 2 の API のみを利用する方法です。このやり方では、table
内の tbody
から、各 tr
をイテレートし、その行にある 2 番目のセルを見つけるという操作を行うことになります。
var table = document.getElementById("score"); var groups = table.tBodies; var rows = null; var cells = []; for (var i = 0; i < groups.length; i++) { rows = groups[i].rows; for (var j = 0; j < rows.length; j++) { cells.push(rows[j].cells[1]); } }
もう一つは、querySelectorAll()
メソッドを利用する方法です。このメソッドを使用すると、スクリプトをとても簡潔に記述することができます。
var cells = document.querySelectorAll("#score>tbody>tr>td:nth-of-type(2)");
このスクリプトは、上記の表が HTML ではなく、XHTML で記述された場合においても適切に機能するでしょう。
仕様書中のすべての図、例、注釈は非規範的なものとなります。また、明示的に非規範的とされたセクションも非規範的なものとなります。それ以外の部分については、規範的なものとなります。
規範的な文章中にあるキーワード must、should、may、recommended は、RFC 2119 で示されるとおりに解釈されます [RFC2119].
この仕様では、次の適合性クラスについても検討され、定義されています。
NodeSelector
インターフェースを実装し、関連するすべての must レベル基準に適合するユーザーエージェントのこと。
この仕様で利用される用語は、セレクター仕様のものです [SELECT].
アルゴリズムとして記された適合性要件や特定のステップについては、結果が同じものである限りどのような方法を用いて実装することも可能です (may)。
Foo
が実際にはインターフェースであるにも関わらず、"Foo
オブジェクト" と表記されることがあります。これは、"Foo
インターフェースをインプリメントするオブジェクト" という意味で用いられています。
この仕様で利用される IDL は、Web IDL で定義された構文を用いています [WEBIDL]。
このセクションは非規範的です。
セレクターの実装状況は、実装により異なる可能性があります。あるセレクターをサポートする実装としない実装が存在する場合、そのセレクターを利用したコードは異なる結果を返すことになるでしょう。
このセクションは非規範的です。
この仕様で定義される API の拡張は 奨励されません (strongly discouraged)。実装者やワーキンググループ、そして他の団体は、拡張について public-webapi@w3.org などの公開フォーラムで議論するべきです。
この仕様を実装するにあたって、ユーザーがセキュリティに関するリスクを負わないようにすることが望まれます。
セキュリティポリシーに違反すると判断される状況に遭遇した場合、実装は処理を中止し、セキュリティ例外を発生させることができます (may)。この仕様や関連する仕様の範囲外でエラーが起こった場合、実装は処理を中止し、言語バインディング固有、または実装固有の例外を発生させることができます (may)。
プライバシーに関して考えられる懸念のひとつに、履歴の取得が挙げられます。セレクター [SELECT] の :visited
擬似クラスをクエリーにかけたとき、訪問済みのリンクを取得できることが問題視されています。
しかしながら、これは古くからある問題です。訪問済みリンクの取得は、CSS と getComputedStyle()
など既存の DOM API により取得することができるからです [DOM-LEVEL-2-STYLE]。
次の例では、vlinks がユーザーが訪れたリンクを取得します。悪意を持った製作者はここから URI を取得し、悪用することができます。
var vlinks = document.querySelectorAll(":visited"); for (var i = 0; i < vlinks.length; i++) { doSomethingEvil(vlinks[i].href); }
セレクター で定義されている ([SELECT], section 6.6.1) 通り、ユーザーエージェントはすべてのリンクを未訪問のリンクとして扱うことができます (may)。実装は、サポートされるセレクターについて、その他の用途と一貫性を持たせ挙動することが推奨されます (recommended)。
NodeSelector
インターフェースDOM Level 3 Core で定義されるインターフェース Document
, DocumentFragment
, Element
をインプリメントするオブジェクトは、次の NodeSelector
インターフェースもインプリメントする必要があります (must) [DOM-LEVEL-3-CORE]。
[NoInterfaceObject] interface NodeSelector { Element querySelector([Null=Empty, Undefined=Empty] in DOMString selectors); NodeList querySelectorAll([Null=Empty, Undefined=Empty] in DOMString selectors); };
querySelector
メソッドの定義で使われる 最初 (first) とは、document order で最初のもの (first in document order) を表します。document order は、該当する DOM ツリーまたはそのサブツリーにおける、深さ優先の先行順走査 (depth-first pre-order traversal) を表します。コンテキストノード (context node) は、メソッドが呼び出されるノードを表します。ノードのサブツリー (node’s subtree) は、コンテキストノード の子孫要素のツリーを表します。マッチする Element
ノード (matching Element
node) は、セレクターの要素マッチング規則 [SELECT] に則り、メソッドに渡されたセレクター (selectors) にマッチする Element
ノードを表します。
NodeSelector
インターフェースの querySelector()
メソッドは、呼び出されたときに、ノードのサブツリー中で与えられたセレクターに最初にマッチする Element
ノードを返す必要があります (must)。もし該当するノードが存在しない場合、このメソッドは null
を返す必要があります (must)。
NodeSelector
インターフェースの querySelectorAll()
メソッドは、呼び出されたときに、ノードのサブツリー中で与えられたセレクターにマッチするすべての Element
ノードを、document order で格納した NodeList
として返す必要があります (must)。もし該当するノードが存在しない場合、このメソッドは空の NodeList
を返す必要があります (must)。
querySelector()
と querySelectorAll()
は セレクター (selectors) を引数にとります ([SELECT], section 5)。メソッドの caller は、妥当なセレクターを渡す必要があります (must)。セレクターは、解決される必要のある名前空間接頭辞 を利用してはいけません (must not)。
擬似要素をセレクターに使うことは可能ですが、これは文書中のどの要素にもマッチしないため、何も要素を返さないことになります。このため、製作者はこの使用で定義されるメソッドにおいて、擬似要素を含むセレクターを利用しないことが推奨されます。
実装はまず、引数のセレクター (selectors) から先頭と最後の 空白文字 (whitespace) を取り除く必要があります (must)。そして、セレクターの文法 ([SELECT], section 10) にしたがって値を処理する必要があります (must)。もし、引数に与えられたセレクター (selectors) が null
または undefined
である場合、実装は空文字列が渡されたものとして考える必要があります (must)。セレクターは当該要素が存在する全 DOM ツリーに対し評価されます。与えられたセレクターが 不正 ([SELECT], section 13) であるとき、実装は 例外 SYNTAX_ERR
を発生させる 必要があります ([DOM-LEVEL-3-CORE], section 1.4) (must)。
CSS をサポートするユーザーエージェントにおいて、実装は CSS でサポートするセレクターと同じサポートを、API に対しても行うべきです (should)。
querySelectorAll()
メソッドから返される NodeList
オブジェクトは 動的 (live) ではなく、静的 (static) である必要があります ([DOM-LEVEL-3-CORE], section 1.1.1) (must)。元文書の構造が変化しても、その変化が NodeList
オブジェクトに反映されることは許されていません (must not)。つまり、返されるオブジェクトは、リストが生成された時点で文書に存在していたノードに対しクエリをかけ、マッチする Element
ノードを取得することを意味します。
セレクターが 解決される必要のある名前空間接頭辞 を含む場合、実装は 例外 NAMESPACE_ERR
を発生させる 必要があります ([DOM-LEVEL-3-CORE], section 1.4) (must)。
解決される必要のある名前空間接頭辞 (namespace prefix needs to be resolved) とは、名前空間コンポーネントが (null 名前空間を表す) 空の状態 (例: |div
) であるものや、どの名前空間をも表すアスタリスク (例: *|div
)、このどちらにも当てはまらないものを表します。アスタリスクや空名前空間接頭辞は解決される必要がないため、セレクターの名前空間構文をサポートする実装は、これら二つをサポートする必要があります [SELECT] (must)。
セレクターの名前空間構文をサポートしない実装は、代わりに例外 SYNTAX_ERR
を投げることがあります。これは、サポートしない実装において、その構文が不正なものとして扱われるからです。
DOM3 Core では、インターフェースのサポート状況を確かめたり、インターフェースの実装を得たりするため、feature 文字列 (feature strings) を利用するいくつかのメソッドを定義しています ([DOM-LEVEL-3-CORE], section 1.3.6)。DOM アプリケーションはこのメソッドの引数 feature に "Selectors-API
" を、そして version には "1.0
" を指定することで利用することができます。
これらの引数を hasFeature
メソッドに与えたとき、適合する実装は true
を返す必要があります (must)。しかし、true
が返されたとしても、実装が完全に仕様に適合しているわけではなく、また false
が返されたとしても、実装がサポートしている可能性もあります。よって、このメソッドの仕様は奨励されません。
このセクションで解説するコードは、次の XHTML 文書を利用しています。
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Selectors API Example</title> </head> <body> <div id="foo"> <p class="warning">This is a sample warning</p> <p class="error">This is a sample error</p> </div> <div id="bar"> <p>...</p> </div> </body> </html>
メソッドは、複数のセレクター (カンマ区切り) を引数に持つことができます。次の例は、class に "error
" または "warning
" を持つ p
要素すべてを選択します。
var alerts = document.querySelectorAll("p.warning, p.error");
querySelector()
メソッドも複数のセレクターを引数に持つことができますが、返すのはマッチしたもののうち最初の要素のみとなります。
var x = document.querySelector("#foo, #bar");
x は、ID に foo
または bar
を指定された要素のうち、最初に現れたものを格納します。このセクションの先頭にあるサンプル文書に対しクエリーを実行すると、ID に foo
を持つ div
要素が選択されます。なぜなら、この要素は document order 上で最初にマッチするからです。
二つのメソッドは、要素上から呼び出すこともできます。次の例では、イベントハンドラーが要素に対して登録されているものとします。なので、このメソッドはイベントのターゲットとなる要素から呼び出されます。
function handle(evt) { var x = evt.target.querySelector("span"); ... // Do something with x }
しかし、メソッドは要素から呼び出されますが、セレクターは文書全体を評価します。次の例において、body
要素は div
要素の子孫でないにも関わらず、メソッドは div
要素の子である p
にマッチします。
var div = document.getElementById("bar"); var p = bar.querySelector("body p");
次のようなナビゲーションメニューを持つ文書があるとします。
<ul class="nav"> <li><a href="/">Home</a></li> <li><a href="/products">Products</a></li> <li><a href="/about">About</a></li> </ul>
次の例は、すべての li
要素を選択し、その結果の NodeList
をイテレートする様子を表しています。
var lis = document.querySelectorAll("ul.nav>li"); for (var i = 0; i < lis.length; i++) { process(lis.item(i)); }
ECMAScript では、NodeList
を配列として扱うことが可能なため、次のように書き換えることができます。
for (var i = 0; i < lis.length; i++) { process(lis[i]); }
メソッドから返される NodeList
オブジェクトは動的ではないため、DOM に行われた変更はリストの内容に変更を及ぼすことがありません。たとえば、前の例で次の関数 process()
を実行したとします。
function process(elmt) { elmt.parentNode.removeChild(elmt); }
このコードは、DOM から選択された要素ひとつずつを削除します。しかし、各要素は NodeList
に残ります。もしリストが動的な NodeList
であれば、DOM から要素を削除したときに、リストの要素も変更され、関連する要素のインデックスも調整されるでしょう。結果として、ループにおいて意に反する効果が行われます。これは、全ての要素が処理されていないからです。
編集者はこの仕様の策定に当たり貢献してくれた、次の方々に感謝しています (ファーストネーム順)。
提案や修正を送り、この仕様に貢献してくれた方々にも感謝しています。