今天,向大家介绍DOM脚本编程领域一项令人激动的进步——W3C Selector API。迄今为止,在使用DOM2级API的前提下,要想从DOM中取得HTML元素,只能使用document.getElementById,或者使用document.getElementsByTagName然后再手工编码进行筛选。随着CSS的普及,JavaScript开发人员不断提到一个显而易见的问题:“为什么浏览器不能提供一种快捷方法,用来选择与CSS选择符匹配的HTML元素呢?”
于是,上述Selector API定义了querySelector和querySelectorAll方法,它们就以CSS选择符为参数,分别返回匹配的第一个元素和所有匹配元素的StaticNodeList(静态节点列表)。这两个方法既可以通过document对象调用,以便在整个文档范围内查询目标元素,也可以通过个别的HTML元素调用,以便只在该元素的后代元素中查询目标元素。
下面我们看一个简单的HTML示例,以此演示如何使用Selector API:
- <ul id="menu">
- <li>
- <input type="checkbox" name="item1_done" id="item1_done">
- <label for="item1_done">bread</label>
- </li>
- <li class="important">
- <input type="checkbox" name="item2_done" id="item2_done">
- <label for="item2_done">milk</label>
- </li>
- <!-- 更多列表项 -->
- </ul>
我们的任务是通过脚本选中所有类名为“important”的列表项中的复选框(checkbox)。如果使用DOM2级API的方法,可以这样来做:
- var items = document.getElementById(
- 'menu'
- ).getElementsByTagName(
- 'li'
- );
- for(var i=0; i < items.length; i++) {
- if(items[i].className.match(/important/)) {
- if(items[i].firstChild.type == "checkbox") {
- items[i].firstChild.checked = true;
- }
- }
- }
而使用新的选择符API,则可以将上面代码简化为:
- var items = document.querySelectorAll(
- '#menu li.important input[type="checkbox"]'
- );
- for(var i=0; i < items.length; i++) {
- items[i].checked = true;
- }
是不是更简单方便了?注意,querySelector和querySelectorAll方法还支持组合选择符——即由逗号分隔的多个选择符。目前,将会支持Selector API的浏览器包括Safari3.1、IE8 beta、Firefox3.1。Opera也在积极添加对该API的支持。
假如读者擅长使用各种JavaScript库,那么心里可能会暗自嘀咕:“这些我通过库早就能做到了呀?”事实上,很多人使用各种JavaScript库的原因在很大程度上恰恰是因为这些库实现了通过CSS选择符的查询机制。最近,由于这些库作者们共享了他们的技术,我们发现CSS选择符查询速度又有了明显的提升。那么,使用Selector API还有什么必要呢?一言以蔽之:速度——本地实现的优势就是速度!而且,所有这些JavaScript库都非常看重这一点。jQuery和Prototype正在向使用Selector API的路线上靠拢,而Dojo Toolkit、DOMAssistant和base2则已经在使用该API了。
这3个库之所以能首批利用该API是有原因的。Kevin在他的博客文章“Is Your JavaScript Library Standards Compliant?(你的JavaScript库与标准兼容吗?)”曾专门讨论过这些问题。由于Selector API要求使用标准的CSS选择符,因此如果浏览器不支持某种选择符,那么就无法使用该选择符。这些已经开始利用Selector API的库都是原先只支持标准CSS选择符的。对于这些库而言,支持该API(几乎)就相当于添加如下代码:
- if(document.querySelector) {
- return document.querySelector(selector);
- } else {
- return oldSelectorFunction(selector);
- }
但是,那些支持自定义选择符的库要支持Selector API则远没有那么简单。首先,如果开发人员已经在自己的项目中大量使用了自定义的CSS选择符,那么要想通过该库获得速度提升会很困难,因为这些库必须使用其默认选择符而不能使用Selector API。其次,即使这些库采取某种措施解决了使用自定义选择符的问题,从而支持了Selector API,但该库也必须面对体积增大的风险。
理想的状况下,Selector API是想鼓励人们使用标准CSS选择符,放弃使用自定义选择符。事实上,如果新版的浏览器都能做到足够好,而且新Selector API的性能提升也足以令人侧目,那么自定义选择符功能很可能就会自动转移到辅助库中,而且只在处理遗留代码的兼容性问题时才会用到。
我个人认为,Dean Edwards的base2库实现得最好的。因为base2完全实现了该API,也就是说,开发人员使用base2编写JavaScript时尽可以使用标准的API方法——因为base2只在浏览器不支持标准API时,才会创建querySelector和querySelectorAll方法。这已经是一种非常理想的状态了。但是,不管怎样base2还是在其自定义选择符功能中实现了非标准的“!=”属性选择符(显然是迫于同行的压力),因此在这一点上也有所失分。
无论你是使用还是在编写JavaScript库,新版浏览器对Selector API的支持必将给所有人的开发工作带来直接的性能提升。换句话说,我们都将是受益者,万岁!
Dojo之父Alex Russell在《Mastering Dojo》中为自己的工具箱所作的“广告”:
Dojo基于兼容未来标准的原则而构建,这些标准最终会在所有浏览器中实现,届时开发人员将可以使用又快又简单的本地方法。dojo.query方法就是一个非常好的例子。W3C Selectors API标准描述了一个通过CSS选择符来查询DOM树的API,该Web API工作组规定了一个编程用的方法,即querySelectorAll。querySelectorAll方法和dojo.query方法一样,都返回一个节点数组。虽然Dojo的CSS查询引擎速度很快,但通过让查询语法与CSS语法保持一致,有效地解决了始终要依赖该引擎的问题。事实上,在完全支持上述API的浏览器中,Dojo会使用querySelectorAll方法。相信dojo.query成为querySelectorsAll的包装方法只是迟早的事——只不过通过包装它还会为返回的节点数组添加一些甜美的语法糖(syntactic sugar)。最好的结果,就是上述API成为真正的标准,不再改变,开发人员也会因此全都享受到由C++实现的查询引擎的快捷与便利,同时,更不会因向后兼容问题而感到烦恼。对于Dojo用户而言,选择这个关注Web未来发展的工具箱,就等于已经享受到了上述好处。
事实上,Alex Russell确实是W3C Selector API的一个重要建议人。