请参阅本文件的勘误表,其中可能包括一些规范性更正。
另请参阅翻译版本。
版权 © 2006-2013 W3C® (MIT, ERCIM, Keio, Beihang),保留所有权利。适用 W3C 责任声明、商标政策以及文档使用规则。
选择器(Selectors)在 CSS 中被广泛使用,它们是匹配树结构中元素的模式 [SELECT][CSS21]。Selectors API 规范定义了一组方法,用于通过匹配一组选择器来从 DOM 中获取 Element 节点。通常希望在文档中对特定元素集合执行 DOM 操作。这些方法简化了获取特定元素的过程,尤其相比过去所定义并使用的更为冗长的技术。
本节描述本文档在发布时的状态。其他文档可能会取代本文档。当前的 W3C 出版物列表和本技术报告的最新修订版可在 https://w3org.cn/TR/ 的 W3C 技术报告索引中找到。
本规范是 已被取代的推荐稿。已有更新的规范,建议在新采纳时使用该规范代替本规范。
出于 W3C 专利政策的目的,此已被取代的推荐稿拥有与活动推荐稿相同的地位;它保留许可承诺,并仍可作为旧的——可能仍在使用的——实现的参考,但不建议用于未来的实现。新的实现应遵循 活标准(Living Standard)的 DOM 规范。
Web Applications (WebApps) 工作组已经开发了一套完整的Selectors API 测试套件,并展示了各实现之间特性的互操作性。请参阅工作组的实现报告。
请将对本文件的评论发送至 public-webapps@w3.org(公共归档),主题请加上 [selectors-api]。注意:在 2008 年中之前使用的是不同的邮件列表,旧消息请在此处归档。
本文件由Web Applications 工作组编写。可在此处获取本文件的完整变更列表。
本文件由依据 2004 年 2 月 5 日 W3C 专利政策 运作的工作组制作。W3C 维护一份 公开专利披露列表,列出与工作组交付成果相关的所有专利披露;该页面还包含专利披露的说明。若个人实际了解某项专利并认为其中包含 关键权利要求,须按照 W3C 专利政策第 6 节的要求进行披露。
本节是非规范性的。
本规范提供了基于是否匹配给定选择器来选择和测试元素的方法。使用这些方法,比起随后对 getElementsByTagName() 等其他方法的返回结果进行过滤,更容易根据特定条件匹配一组 Element 节点。
本节是非规范性的。
一些 ECMAScript [ECMA-262-5.1] 示例
这是一个使用 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>
若要获取表格中包含结果的单元格(例如用于在图表上绘制数值),至少有两种可行的方法。仅使用 DOM Level 2 的 API 时,需要如下脚本:遍历 table 中每个 tbody 的每个 tr,并获取每行的第二个单元格。
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)");
请注意,这段脚本操作的是 DOM,且与创建文档时所使用的语法无关。因此,它同样适用于使用符合规范的 XHTML 创建的等价表格,或是通过 DOM API 动态创建并插入文档的表格。
本规范中的所有图示、示例和注释均为非规范性内容,且所有明确标记为非规范性的章节亦如此。除这些之外的全部内容均为规范性要求。
本文件规范部分出现的关键词 must、must not、should、may 与 recommended,应按 RFC 2119 [RFC2119] 的定义进行解释。
本规范定义(并且考虑)以下符合性类:
本规范中使用的术语来源于 Selectors [SELECT]。
以下功能在 DOM Level 3 Core 规范中定义 [DOM-LEVEL-3-CORE]:
Document 接口。DocumentFragment 接口。Element 接口。NodeList 接口。以算法或具体步骤表述的符合性要求 may 以任何方式实现,只要最终结果等价即可。
本规范使用的 IDL 采用 Web IDL [DOM-BINDINGS] 中定义的语法。
“Foo 对象”这一表述有时用于代替更精确的“实现了 Foo 接口的对象”。
本节是非规范性的。
某些实现可能对 Selectors 的支持程度不同。如果实现缺少对某些选择器的支持,则使用这些选择器时会导致返回的结果不符合预期。建议作者检查这些 API 抛出的 DOM 异常,并提供回退机制以实现优雅降级。
本节是非规范性的。
强烈不建议对本规范定义的 API 进行扩展。实现者、工作组及其他感兴趣的方应在公共论坛(如 public-webapps@w3.org)上讨论扩展事宜。
预计实现本规范不会为用户引入新的安全风险。
如果实现检测到任何会违反安全策略的情况,实现 may 中止执行并抛出安全异常。如出现本规范或其他相关规范未直接覆盖的错误条件,实现 may 中止并抛出相应语言绑定或实现特定的异常。
历史记录泄露是一种潜在的隐私问题,因为 Selectors 中的 :visited 伪类 [SELECT] 允许作者查询哪些链接已被访问。
这并非新问题,已有的 CSS 与 DOM API(例如 getComputedStyle() [DOM-LEVEL-2-STYLE])已经可以被利用。
在本示例中,vlinks 将获取用户已访问的链接列表。作者随后可以获取这些链接的 URI 并可能基于此进行利用。
var vlinks = document.querySelectorAll(":visited");
for (var i = 0; i < vlinks.length; i++) {
doSomethingEvil(vlinks[i].href);
}
正如在 Selectors([SELECT] 第 6.6.1 节)中所定义,用户代理 may 将所有链接视为未访问链接。recommended 实现应在行为上与用户代理对其他 Selectors 的支持保持一致。
本规范中方法定义使用的术语 first 表示“文档顺序中的第一个”。术语 document order 指对 DOM 树(或子树)的深度优先前序遍历。context node 指调用该方法的节点。subtrees 指 context node 的所有后代元素集合。matching Element node 指与传入方法的 selector string(selectors)匹配的 Element 节点,匹配规则遵循 Selectors [SELECT] 中的定义。
partial interface Document {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
};
partial interface DocumentFragment {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
};
partial interface Element {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
};
querySelector 与 querySelectorAll 方法接受的 selectors 参数为选择器字符串。
在 Document、DocumentFragment 与 Element 接口上的 querySelector() 方法must 返回 subtrees 中 first 个匹配的 Element 节点;若不存在匹配的 Element,该方法must 返回 null。
在 Document、DocumentFragment 与 Element 接口上的 querySelectorAll() 方法must 返回一个 NodeList,其中包含 subtrees 中所有匹配的 Element 节点,按 document order 排列;若没有匹配节点,则返回空的 NodeList。
当调用 querySelector 或 querySelectorAll 任何一个时,实现必须遵循以下步骤:
令 parsed selector 为对 解析选择器 算法运行后得到的结果,其中输入为 selectors。
随后实现必须运行 评估选择器 算法,对指定 树 或 subtrees 中的元素节点使用 parsed selector 进行匹配,以返回被调用方法所需的结果。
由 querySelectorAll() 方法返回的 NodeList 对象must 为静态(static)而非实时(live)[DOM-LEVEL-3-CORE],第 1.1.1 节所述。后续对底层文档结构的更改must not 反映在该 NodeList 中。也就是说,该对象在创建时即捕获了当时文档中所有匹配的 Element 节点列表。
选择器字符串是一组或多组选择器组([SELECT] 第 5 节),可被空白字符包围,并匹配 dom_selectors_group 产生式。
dom_selectors_group : S* [ selectors_group ] S* ;
selectors_group 的产生式在 Selectors Level 3([SELECT] 第 10.1 节)中定义。这类选择器组should not 使用需要解析的命名空间前缀。
实现者应当注意:如果将 null 或 undefined 作为 selectors 参数传入,应按照 WebIDL [DOM-BINDINGS] 的定义进行处理。作者应避免传入这些值。
作者应注意:虽然在选择器中使用伪元素是允许的,但它们不会匹配文档中的任何元素,因而不会返回任何元素。建议作者避免在传递给本规范定义的方法的选择器中使用伪元素。
解析选择器(parse a selector)的步骤如下:
令 selectors 为本算法的输入——即选择器字符串。
令 result 为依据前述 dom_selectors_group 语法对 selectors 进行解析后得到的选择器组。
如果 result 为无效([SELECT] 第 12 节),则抛出 SYNTAX_ERR 异常([DOM-LEVEL-3-CORE] 第 1.4 节),并中止本算法。
评估选择器(evaluate a selector)的步骤如下:
令 element 为正在评估的元素。
令 selector group 为传入本算法的选择器组。
如果 selector group 中的任意选择器匹配 element,返回 true;否则返回 false。
选择器在整个包含该元素的 DOM 树的上下文中进行评估。
如果用户代理同时支持某个层级的 CSS,实现should 在这两个 API 与 CSS 中提供相同的选择器集合。
如果选择器组中包含需要解析的命名空间前缀,实现must 抛出 SYNTAX_ERR 异常([DOM-LEVEL-3-CORE] 第 1.4 节)。
本规范并未提供解析任意命名空间前缀的支持。不过,未来版本可能会考虑加入命名空间前缀解析机制。
当命名空间组件既非空(例如 |div,表示空命名空间)也非星号(例如 *|div,表示任意命名空间)时,即视为需要解析的命名空间前缀。因为星号或空前缀不需要解析,支持 Selectors 中命名空间语法的实现must 支持这两种情况。[SELECT]
以下示例使用了此示例 XHTML 文档。
<html xmlns="https://w3org.cn/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 的第一个元素;若不存在则为 null。在上面的示例文档中,它会选取 ID 为 foo 的 div 元素,因为它在文档顺序中位列第一。参数中选择器的顺序并不影响结果,反转顺序仍会得到相同的结果,如下所示:
var x = document.querySelector("#bar, #foo");
这些方法也可以在元素上调用。下面的示例假设事件处理函数已在某元素上注册,因此该方法在事件的目标元素上被调用。
function handle(evt) {
var x = evt.target.querySelector("span");
...
// Do something with x
}
即使方法在元素上调用,选择器仍然在整个文档的上下文中进行评估。在下面的示例中,方法仍然会匹配 div 元素的子元素 p,尽管 body 并不是 div 的后代。
var div = document.getElementById("bar");
var p = div.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 中移除元素的同时也会从列表中移除该元素并调整后续元素的索引,这会对循环产生不利影响,因为并非所有已选元素都会被处理。
在包含多个命名空间元素的文档中,可能出现不同命名空间的元素共享相同本地名的情况。由于本 API 并未原生支持选择器的命名空间解析机制,要从特定命名空间获取此类元素列表(并排除其他命名空间的元素)需要额外的过滤处理。下面的示例展示了一个文档,其中既有 SVG 命名空间又有 XHTML 命名空间的 video 元素。
<svg id="svg1" xmlns="https://w3org.cn/2000/svg"
xmlns:xlink="https://w3org.cn/1999/xlink">
<video id="svgvideo1" xlink:href="myvideo.ogg" width="320" height="240"/>
<foreignObject width="100" height="100">
<video id="htmlvideo1" src="myvideo.ogg" xmlns="https://w3org.cn/1999/xhtml">No video1</video>
</foreignObject>
</svg>
以下脚本演示了如何先选取 video 元素,然后依据其命名空间过滤掉不需要的元素。
var elms = document.querySelectorAll("svg video");
var result = new Array();
var svgns = "https://w3org.cn/2000/svg"
for(var i = 0; i < elms.length; i++) {
if(elms[i].namespaceURI == svgns) {
result.push(elms[i]);
}
}
编辑们要特别感谢以下为本规范作出贡献的人员(按名字首字母排序)
Adam van den Hoven, Alan Gresley, Alex Russell, Arthur Barstow, Aryeh Gregor, Björn Höhrmann, Boris Zbarsky, Cameron McCormack, Charles McCathieNevile, Chris Wilson, Christophe Jolif, Daniel Glazman, Daniel Schierbeck, Dave Massy, David “liorean” Andersson, David Greenspan, David Håsäther, Dean Jackson, Doug Schepers, Erik Dahlström, Francois Remy, Hallvord R. M. Steen, Henri Sivonen, Ian Hickson, Ivan Enderlin, Jean‑Yves Bitterlich, Jim Ley, João Eiras, John Resig, Jon Ferraiolo, Jonas Sicking, Jorgen Horstink, Kang‑Hao (Kenny) Lu, Karl Dubost, Kartikaya Gupta, L. David Baron, Maciej Stachowiak, Magnus Kristiansen, Marat Tanalin, Martijn, Masataka Yakura, Mihai Sucan, Mohamed Zergaoui, Nicholas C. Zakas, Nicolas Mendoza, Norbert Lindenberg, Philip Taylor, Robert Sayre, Robin Berjon, Sander, Sergey Ilinsky, Simon Pieters, Steven Pemberton, Stewart Brodie Tab Atkins Jr., Tarquin Wilton‑Jones, Travis Leithead, and William J. Edney
特别感谢 John Resig 与 Øyvind Stenhaug 对测试套件所作的卓越贡献。
感谢所有通过提交建议和纠正帮助改进本规范的朋友们。