標籤:des blog http io ar 使用 sp for on
使用CSS選取器擷取元素 -- querySelector,querySelectorAll(HTML5)標準
- W3C Selector API Level 1為
Document,DocumentFragment和Element追加了querySelector和querySelctorAll,原型為Element? querySelector(DOMString selectors)和 NodeList querySelectorAll(DOMString selectors),說明了匹配的演算法
- W3C Selector API 2又追加了
find和findAll,但目前在各大瀏覽器裡暫無實現(這個標準目前還未進入recommendation)。
- WHATWG DOM 將
querySelector及querySelectorAll定義在了interface ParentNode並聲明Document, DocumentFragment及Element均需實現這個interface,原型為Element? querySelector(DOMString selectors)與[NewObject] NodeList querySelectorAll(DOMString selectors),並定義了scope-match的步驟。注意interface ParentNode還有用於擷取相對位置的元素的兩個方法query和queryAll,但目前在各大瀏覽器裡暫無實現。
- DOM4也新增了
interface ParentNode,和WHATWG類似
注意點
- 標準裡強調了
querySelectorAll返回的一定是一個static NodeList -- 也就是說如果將它的返回結果儲存下來,當文檔更新時,儲存的NodeList裡的元素不會跟著更新。
- W3C Selector API Level 1 規定當傳入的CSS選取器不合法時,會拋出
SYNTAX_ERR異常。Selector API Level 2 和 WHATWG 改為了 SyntaxError。
- 按照W3C Selector API Level 1的提示,在選取器裡使用pseudo-elements(目前只有
:after,:before,:first-letter,:first-line,:selection)將不會匹配到任何元素,另外出於保護隱私的考慮,標準也推薦將所有連結視為未訪問,即:visited不會匹配到任何元素。
- 無匹配元素時,
querySelector返回null,querySelectorAll返回空的NodeList
- 有多個匹配元素時,
querySelector返回按照document order(先序DFS)遍曆到的第一個元素,querySelectorAll返回按照 document order 排序的NodeList
相容性
IE 9+及其他瀏覽器的現行版本正常支援包括CSS3的選取器,IE8支援簡單的 CSS2 選取器(如:不支援空格表示的後代)
WebKit 程式碼分析
ContainerNode 就是 WHATWG 裡描述的 interface ParentNode,ContainerNode的querySelector和querySelctorAll實際上分別調用SelectorQuery的queryFirst和queryAll(參考WebCore/dom/ContainerNode.cpp),它們又分別調用SelectorDataList的queryFirst和queryAll(注意SingleElementExtractorSelectorQueryTrait和AllElementExtractorSelectorQueryTrait這兩個使用模版達到類似動態類型的寫法挺有趣的),通過execute來對ContainerNode的子節點匹配CSS。execute裡就是CSS選取器的代碼,裡面還有相當一部分 JIT 的最佳化,這裡就不展開分析了。
在queryFirst用於SelectorQueryTrait的 template specialiation 的 SingleElementExtractorSelectorQueryTrait裡,shouldOnlyMatchFirstElement設為true,注意execute用於匹配CSS的其他方法基本都會在第一次找到匹配元素的時候檢查shouldOnlyMatchFirstElement確定是否立刻儲存匹配結果並返回(使用elementDescendants達到先序DFS,elementDescendants最終也是和getElementsByID的實現一樣使用到了NodeTraversal),這樣就達到了標準裡提到的返回先序DFS遇到的第一個匹配元素的要求。而queryAll使用了StaticElementList(StaticNodeList),來為儲存匹配元素的Vector(屬於WTF)建立一個 static 的快照用於返回(參見WebCore/dom/SelectorQuery.cpp)
NCZ的部落格上討論了為何StaticNodeList會相對慢很多(不過上面用的是幾年前的代碼,現在的代碼用的是WTF的Vector的swap(底層調用std::swap)通過交換元素來實現複製,參見Source/WTF/wtf/Vector.h)
jQuery也有一個關於querySelectorAll效能問題的 Open issue。
跟隨標準與Webkit源碼探究DOM -- 擷取元素之querySelector,querySelectorAll