打印

querySelector 和 querySelectorAll 方法浏览器实现无误,避免将其与 JQuery 的选择器混淆

作者:钱宝坤

规范定义

querySelector 和 querySelectorAll 方法是 W3C Selectors API Level 1 规范中定义的。他们的作用是根据 CSS 选择器规范,便捷定位文档中指定元素。

目前几乎主流浏览器均支持了他们。包括 IE8(含) 以上版本、 Firefox、 Chrome、Safari、Opera。

querySelector 和 querySelectorAll 在规范中定义了如下接口:

module dom { [Supplemental, NoInterfaceObject] interface NodeSelector { Element querySelector(in
                DOMString selectors); NodeList querySelectorAll(in DOMString selectors); }; Document implements
                NodeSelector; DocumentFragment implements NodeSelector; Element implements NodeSelector; };

其实就是任何 NodeList 、Element 的实例对象和 Document DocumentFragment 的实例对象都有这两个方法。如:

  • document.querySelectorAll
  • document.querySelector
  • nodeList.querySelectorAll
  • nodeList.querySelector
  • element.querySelectorAll
  • element.querySelector

querySelectorAll 返回符合 Selector 条件的所有节点内容,是个 NodeList;querySelector 仅返回符合 Selector 条件的第一个节点内容,是个 Node。

如何用 querySelectorAll 或 querySelector呢?来看个例子:

HTML CODE: <DOCTYPE html> <html> <head> <title>Selectors API
                Example</title> </head> <body> <div id="foo"> <p
                class="warning">This is a sample warning</p> <p >This is a sample
                error</p> </div> <div id="bar"> <p>...</p> </div>
                </body> </html> JAVASCRIPT CODE: var alerts = document.querySelectorAll("p.warning,
                p.error"); // 返回 [<p class="warning">This is a sample warning</p>,<p>This is a sample
                error</p>]

哈哈,是不是很好用啊。

JQuery 的 Selector

那我们怎么兼容低版本的浏览器呢?不用着急,有 JQuery 呢,这个火爆的东东早早就实现了 Selectors。

JAVASCRIPT JQuery CODE: var alerts = $("p.warning, p.error"); // 返回 [<p
                class="warning">This is a sample warning</p>,<p >This is a sample
                error</p>]

这与使用 和querySelectorAll 结果一致。

两者间差异

再用用 element.querySelectorAll 看看:

JAVASCRIPT CODE: var foo= document.getElementById("foo"); foo.querySelectorAll("div >
                p"); // 返回 [<p class="warning">This is a sample warning</p>,<p >This is
                a sample error</p>]
JAVASCRIPT JQuery CODE: var foo=
                document.getElementById("foo"); $(foo).find("div > p") // 返回 []

玩砸了……为什么两者返回结果不一致了呢?

我们看下传入的选择器字符串含义,不就是在 <div id="foo"> 节点下寻找 div 标签下的 p 标签么?

<div id="foo"> 节点下没有 div 标签了,当然应该返回一个空 nodeList。JQuery 返回的结果是正确的。很奇怪,难道说所有实现了 querySelector和 querySelectorAll 方法的浏览器都没遵守规范?这也太坑爹了!!

等等,我们还是先看看规范定义怎么说:

querySelectorAll : when invoked, return a NodeList containing all of the matching
                  Element nodes within the node’s subtrees, in document order. 还有一句 :Even though the
                  method is invoked on an element, selectors are still evaluated in the context of the entire
                  document.

结合起来看,规范定义为选择器在以整个文档为基准,查找全部符合选择器描述的节点,判断返回的 NodeList 是否在 Element 子树内,如果是在 Element 子树内,则这些节点组成 NodeList 返回,其排序需与文档原始节点排序一致。

根据这个定义,我们看浏览器实现:

  • 先是在文档中找到所有处于 div 标签内的 p 子节点,他们是 [<p class="warning">This is a sample warning</p>, <p >This is a sample error</p>,<p>...</p>];
  • 然后对比 <div id="foo"> 节点的子树中是否含有这些 p 元素。<div id="foo"> 节点的子树中仅含有[<p class="warning">This is a sample warning</p>, <p >This is a sample error</p>],那么就返回他们吧。这与之前问题例子返回结果一致。

这么说,浏览器实现没错?好吧,我们可以再做个更离谱的测试来看看:

JAVASCRIPT CODE: var foo= document.getElementById("foo"); foo.querySelectorAll("html
                body div > p"); // 返回 [<p class="warning">This is a sample
                warning</p>,<p >This is a sample error</p>]

这次的例子是在 <div id="foo"> 节点下寻找 html 标签中的body 标签中的 div 标签的直接子标签 P。

他的返回结果依然是 [<p class="warning">This is a sample warning</p>,<p >This is a sample error</p>]

这与规范说明一致。

这么说,浏览器本身实现并没有问题,而是JQuery有问题了?其实这也并不尽然,JQuery 本身并没有宣布遵守 W3C Selectors API Level 1 规范实现查找结果,他的选择器 API 实现是私有的。

对于 Element 下的选择器范围,JQuery 认为是从当前元素开始查找,返回符合的结果集。而规范恰恰指出的是选择器只针对当前文档,选择出的结果集再与当前元素的子树比较。

正是由于以上的不同导致了他们返回结果不一致。

建议

切记不要在实际使用中混淆 W3C Selectors API Level 1 规范中选择器的实现机理和 JQuery 中选择器实现机理,它们是不同的。

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE8
IE9
Firefox 4.0.1
Chrome 12.0.742.100
Safari 5.0.5
Opera 11.11
测试页面:  
本文更新时间: 2011-09-26

关键字

JQuery selector querySelectorAll querySelector 选择器