Copyright © 2006-2007 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
Selectors, which are widely used in CSS, are patterns that match against
elements in a tree structure [Selectors] [CSS21]. The Selectors API specification
defines methods for retrieving Element
nodes from the DOM by matching against a group of
selectors. It is often desirable to perform DOM operations on a specific
set of elements in a document. These methods simplify the process of
aquiring specific elements, especially compared with the more verbose
techniques defined and used in the past.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This is a Last Call Working Draft of "Selectors API". The W3C Membership and other interested parties are invited to review the document and send comments to public-webapi@w3.org (public archive) with [selectors-api] in the subject, through 06 January 2008.
Web content and browser developers are encouraged to review this draft. This draft is considered relatively stable and is expected to progress to Candidate Recommendation after the review period. The editor’s copy of this specification is available in W3C CVS. A detailed list of changes is also available from the CVS server.
This document was developed by the Web API Working Group. The Working Group expects to advance this Working Draft to Recommendation Status.
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This section is non-normative.
This specification introduces two methods that take a group of selectors
(often simply referred to as a selector) as an argument and return the
matching elements [Selectors].
With these methods, it is easier to match a set of Element
nodes based on specific criteria. So instead of having to filter the
result of a getElementsByTagName()
call, authors can directly
“filter” in the query.
This section is non-normative.
Some ECMAScript [ECMAScript] examples:
This is an example HTML table.
<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>
In order to obtain the cells containing the results in the table, which
might be done, for example, to plot the values on a graph, there are at
least two approaches that may be taken. Using only the APIs from DOM
Level 2, it requires a script like the following that iterates through
each tr
within each tbody
in the
table
to find the second cell of each row.
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]); } }
Alternatively, using the querySelectorAll()
method,
that script becomes much more concise.
var cells = document.querySelectorAll("#score>tbody>td:nth-of-type(2)");
All diagrams, examples and notes in this specification are non-normative, as are all sections explicitly marked non-normative. Everything else in this specification is normative.
The key words must, should, and may in the normative parts of this document are to be interpreted as described in RFC 2119 [RFC2119].
The following conformance classes are defined (and considered) by this specification:
DocumentSelector
, ElementSelector
and StaticNodeList
interfaces described in
this specification and conforms to all must-level
critera that apply to user agents.
NSResolver
interface which conforms to all
must-level critera that apply to the NSResolver
interface.
The terminology used in this specification is that from Selectors [Selectors].
Conformance requirements phrased as algorithms or specific steps may be
implemented in any manner, so long as the end result is equivalent. In
doing so, user agents may assume that the object
implementing the NSResolver
interface (or ECMAScript Function
) returns consistent results
when its lookupNamespaceURI()
method is
invoked.
The construction "Foo
object", where Foo
is
actually an interface, is sometimes used instead of the more accurate
"object implementing the Foo
interface".
This section is non-normative.
Since user agents may optimise the algorithms described in this
specification, and because some may invoke the NSResolver
object more than others,
interoperability concerns may arise if the the NSResolver
object (or ECMAScript
Function
) causes side effects or returns inconsistent results
each time it is invoked.
In the following example, the NSResolver
causes a side effect when
executed.
function resolver(prefix) { sideEffect(); if (prefix == "test") { return "http://example.org/test"; } } var x = document.querySelectorAll("test|*:empty > test|p", resolver)
Some user agents may not need to resolve the namespace
prefix in order to determine that the selector cannot match any
elements, in which case sideEffect()
will not be invoked.
The following example could return inconsistent results each time it is
invoked, depending on the order in which namespace prefixes are resolved
and the number of times the resolver is invoked to for the
foo
prefix.
var i = 0; function resolver(prefix) { var ns = ["http://example.org/foo", "http://example.org/bar", "http://example.org/baz"]; return ns[i++]; } var x = document.querySelectorAll("foo|x, foo|y, bar|z", resolver);
This could result in selecting x
, y
and
z
elements from almost any combination of namespaces that
may be returned. The result is unpredictable and different results may
occur in different user agents.
This section is non-normative.
Extensions of the APIs defined in this specification are strongly discouraged. User agents, Working Groups and other interested parties should discuss extensions on a relevant public forum, such as public-webapi@w3.org.
It is expected that implementing this specification introduces no new security risks for users.
User agents should ensure they remain stable when
facing a hostile NSResolver
object.
Potentially hostile behaviour includes:
History theft is a potential privacy issue because the
:visited
pseudo-class in Selectors [Selectors] allows authors to query which
links have been visited.
This is not a new problem, as it can already be exploited
using existing CSS and DOM APIs, such as getComputedStyle()
[DOM2Style].
In this example, vlinks will aquire a list of links that the user has visited. The author can then obtain the URIs and potentially exploit this knowledge.
var vlinks = document.querySelectorAll(":visited"); for (var i = 0; i < vlinks.length; i++) { doSomethingEvil(vlinks[i].href); }
As defined in Selectors ([Selectors], section 6.6.1), user agents may treat all links as unvisited links, or implement other measures to preserve the user’s privacy.
querySelector()
and querySelectorAll()
MethodsObjects implementing the Document
interface must also implement the DocumentSelector
interface.
Likewise objects implementing the Element
interface must also implement the ElementSelector
interface.
[DOM3Core]
interface DocumentSelector { Element querySelector(in DOMString selectors); Element querySelector(in DOMString selectors, in NSResolver nsresolver); StaticNodeList querySelectorAll(in DOMString selectors); StaticNodeList querySelectorAll(in DOMString selectors, in NSResolver nsresolver); }; interface ElementSelector { Element querySelector(in DOMString selectors); Element querySelector(in DOMString selectors, in NSResolver nsresolver); StaticNodeList querySelectorAll(in DOMString selectors); StaticNodeList querySelectorAll(in DOMString selectors, in NSResolver nsresolver); }
The querySelector()
methods on
the DocumentSelector
interface must, when invoked, return the first
Element
node within the document, in document order (using
depth-first pre-order traversal), that matches the group of selectors
(selectors), if any. Otherwise it must return
null
.
The querySelector()
methods on
the ElementSelector
interface must, when invoked, return the first
Element
node in document order that is a descendant of the
element on which the method was invoked and matches the group of selectors
(selectors), if any. Otherwise it must return
null
.
The querySelectorAll()
methods on the DocumentSelector
interface must, when invoked, return a StaticNodeList
of all the
Element
nodes within the document, in document order, that
match the group of selectors (selectors) in document order, if
any. Otherwise it must return an empty StaticNodeList
.
The querySelectorAll()
methods on the ElementSelector
interface must, when invoked, return a StaticNodeList
of all the
Element
nodes, in document order, that are descendants of the
element on which the method was invoked and matches the group of selectors
(selectors), if any. Otherwise it must return
an empty StaticNodeList
.
Both querySelector()
and querySelectorAll()
take a
group of selectors (selectors) as the first argument and
optionally an NSResolver
(nsresolver) as the second. User agents must use the nsresolver argument to resolve namespace prefixes to namespaces or to get the default namespace. When the nsresolver argument is null
user agents must ignore it. [Selectors]
When using namespace prefixes within selectors or if there
needs to be a default namespace, authors must pass an
NSResolver
object, or a
Function
in the case of ECMAScript, as the argument for nsresolver. Otherwise, authors may set
the argument to null
or omit it if the language binding
permits.
If the given group of selectors (selectors) is invalid, the
user agent must raise
a SYNTAX_ERR
exception ([DOM3Core], section 1.4).
An unresolvable namespace is a namespace that
cannot be resolved because there was no NSResolver
provided or the given NSResolver
does not return a namespace for
the namespace prefix. When an unresolvable
namespace is encountered, the user agent must raise
a NAMESPACE_ERR
exception ([DOM3Core], section 1.4).
The default namespace does not need to be defined.
If an exception is raised by the NSResolver
while resolving namespaces,
processing must be aborted and the exception propagated to the caller.
In languages that support optional arguments for methods, like
ECMAScript, if the nsresolver argument is omitted, the
user agent must act as if the nsresolver argument was set to null
.
Using pseudo-elements in one of the selectors could mean that
nothing is returned for that particular selector when it doesn’t resolve
in one or more Element
nodes.
The methods accept a group of selectors (comma separated) as the
argument. The following example would select all p
elements
in the document that have a class of either
"error
" or "warning
".
var alerts = document.querySelectorAll("p.warning, p.error");
The querySelector()
method also
accepts a group of selectors and it will return the first element that
matches either selector in the group (if any).
var x = document.querySelector("#foo, #bar");
x would contain the first element in the document with an ID
of either foo
or bar
(or both).
The methods can also be invoked on elements. In this example, the method is invoked on an element that is the target of an event listener.
function handle(evt) { var x = evt.target.querySelector("span"); ... // Do something with x }
NSResolver
InterfaceThe NSResolver
interface allows
prefixes to be bound to a namespace URI. The object implementing this
interface is expected to be implemented by authors.
Other specifications may introduce methods that return an
object implementing NSResolver
in
which case it would be implemented by the user agent.
interface NSResolver { DOMString lookupNamespaceURI(in DOMString prefix); };
The lookupNamespaceURI()
method must, when invoked, return the namespace
represented by the prefix (prefix) or the default namespace if
the prefix argument is an empty string.
When the lookupNamespaceURI()
method is
invoked with a prefix as the argument, it must return
the namespace URI for the given prefix. When the lookupNamespaceURI()
method is
invoked with an empty string as the argument (representing the default
namespace), it must do either of the following:
null
, or (if permitted by the language binding) no return
value (undefined
).The lookupNamespaceURI()
must return consistent results each time it is invoked.
Authors are strongly discouraged from writing an
NSResolver
that returns
inconsistent results.
In ECMAScript (and other languages that allow similar constructs), this
object may be implemented as a Function
. In
such languages, user agents must support NSResolver
being implemented as a
Function
.
To resolve namespace prefixes, if there was an
NSResolver
object provided, user
agents must pass the prefix, preserving case, to the
lookupNamespaceURI()
method. User agents must handle prefixes case
sensitively. If there was no NSResolver
object provided, or if the method
returns an empty string or does not return a DOMString
, the
prefix represents an unresolvable namespace.
Otherwise, the return value is the namespace for the given prefix.
The case sensitivity of namespace prefixes is effectively
determined by the implementation of the NSResolver
object that is used to resolve
the namespaces.
In Unicode, caseless matching requires both strings that are
being compared, to be case folded prior to performing a binary comparison
[CaseMap]. However, since case
folding is not the same as simply uppercasing or lowercasing both strings
and because the comparison is being performed by the NSResolver
object implemented by the author,
this specification cannot require case insensitive namespace prefixes.
To get the default namespace, if
there was an NSResolver
object
provided, user agents must invoke the lookupNamespaceURI()
method with the
empty string as the argument. If there is no NSResolver
object provided, or if the method
returns an empty string, null
, undefined
, or
equivalent, then there is no default namespace. Otherwise, if the method
does not return a DOMString
, the default namespace is an unresolvable namespace. Otherwise, the
return value is the default namespace.
The number of times that a user agent may invoke the
lookupNamespaceURI()
method
with each prefix and the order in which prefixes are resolved is not
defined by this specification.
The follwoing examples make use of this sample document.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xml:lang="en"> <head> <title>Selectors API Example</title> </head> <body> <div><svg:svg … > … </svg:svg></div> <p><svg:svg … > … </svg:svg></p> <p><svg xmlns="http://www.w3.org/2000/svg" … > … </svg></p> </body> </html>
The NSResolver
may be
implemented as an object with a lookupNamespaceURI
method.
var resolver = { lookupNamespaceURI: function(prefix) { var ns = { "xh" :"http://www.w3.org/1999/xhtml", "xbl" :"http://www.w3.org/ns/xbl", "svg" :"http://www.w3.org/2000/svg", "math":"http://www.w3.org/1998/Math/MathML" }; return ns[prefix]; } }
For convenience, in ECMAScript, the NSResolver
may also be implemented as a
function. In the above example, namespace prefixes are resolved case
sensitively. If case insensitve namespace prefixes are desired, it is
possible to implement this in the resolver by altering the case of the
prefix before comparison.
function resolver(prefix) { var ns = { "xh" :"http://www.w3.org/1999/xhtml", "xbl" :"http://www.w3.org/ns/xbl", "svg" :"http://www.w3.org/2000/svg", "math":"http://www.w3.org/1998/Math/MathML" }; return ns[prefix.toLowerCase()]; } var svgImages = document.querySelectorAll("xh|p SVG|svg", resolver);
Using the case insensitive namespace resolver, the prefix
xh
will be resolved as
http://www.w3.org/1999/xhtml
and SVG
as
http://www.w3.org/2000/svg
. The svgImages
variable will be a StaticNodeList
containing the two
svg
elements that are descendents of the p
elements.
The default namespace can be specified so that prefixes do not need to be used for selecting elements in a specific namespace. In the following example, the default namespace has been set to the XHTML namespace.
function resolver(prefix) { var ns = { "" :"http://www.w3.org/1999/xhtml", // Default namespace "xbl" :"http://www.w3.org/ns/xbl", "svg" :"http://www.w3.org/2000/svg", "math":"http://www.w3.org/1998/Math/MathML" }; return ns[prefix]; }
Using this resolver, the previous example can be rewritten without
explicitly declaring the XHTML namespace using the xh
prefix.
var svgImages = document.querySelectorAll("p svg|svg", resolver);
In the following example, the foo
namespace is undefined
and will result in an unresolvable namespace. The
NAMESPACE_ERR
exception can be handled using a
try
/catch
block.
try { document.querySelectorAll("foo|bar", resolver); } catch (e) { switch (e.code) { case DOMException.NAMESPACE_ERR: // Namespace error break; case DOMException.SYNTAX_ERR: // Syntax error break; default: // Unknown error } }
StaticNodeList
Interfacetypedef StaticNodeList NodeList;
The StaticNodeList
must be implemented identically to the NodeList
interface as defined in DOM Level 3 Core [DOM3Core] with the exception that the
interface, as the name suggests, is static and not live.
This example demonstrates how to iterate through the items in a
StaticNodeList
.
var lis = document.querySelectorAll("ul.nav>li"); for (var i = 0; i < lis.length; i++) { process(lis.item(i)); }
In ECMAScript, the language binding also allows NodeList
s
and StaticNodeList
s to be
addressed using the array notation, so that loop could be rewritten like
this:
for (var i = 0; i < lis.length; i++) { process(lis[i]); }
Since a StaticNodeList
is
not live, changes to the DOM do not affect the content of the list.
Consider the process()
function called in the previous
examples is defined as follows:
funtction process(elmt) { elmt.parentNode.removeChild(elmt); }
This would cause each selected element to be removed from the DOM, but
each element will remain in the StaticNodeList
. If the list were a
live NodeList
, removing an item from the DOM would also
remove the element from the list and adjust the indexes of subsequent
elements. That would have adverse effects upon the loop because not all
selected elements would be processed.
The editor would like to thank to the following people who have contributed to this specification (ordered on first name):
Thanks to all those who have helped to improve this specification by sending suggestions and corrections. (Please, keep bugging us with your issues!)