你不知道的Jquery的選擇器!

選擇器 Sizzle

Sizzle 是一款純 JavaScript 實現的 CSS 選擇器引擎,它具有以下特性: ‰ 完全獨立,無庫依賴。 ‰ 相較於大多數常用選擇器其性能非常有競爭力。

  • ‰ 壓縮和開啟 gzip 后只有 4 KB。

  • ‰ 具有高擴展性和易於使用的 API。

  • ‰ 支持多種瀏覽器,如 IE 6.0+、Firefox 3.0+、Chrome 5+、Safari 3+、Opera 9+。

W3C Selectors API 規範定義了方法 querySelector() 和 querySelectorAll(),8 它們用於根據

CSS 選擇器規範 定位文檔中的元素,9 但是老版本的瀏覽器(如 IE 6、IE 7)不支持這兩個方 法。在 Sizzle 內部,如果瀏覽器支持方法 querySelectorAll(),則調用該方法查找元素,如果 不支持,則模擬該方法的行為。

Advertisements

選擇器表達式

術語和約定

  • CSS 選擇器表達式,例如,"div>p" 逗號分割的多個選擇器表達式,例如,"div, p"

  • 例如,"div>p" 中的 "div"、"p"

  • 例如,"div" 的類型是 TAG,".red" 的類型是 CLASS,"div.red" 則是 TAG + CLASS。

  • 共有 8 種塊表達式類型:ID、CLASS、NAME、ATTR、TAG、CHILD、POS、PSEUDO 表示塊表達式之間關係的符號,例如,"div>p" 中的 ">"。共有 4 種塊間關係符:">" 父子關係、"" 祖先後代關係、"+" 緊挨著的兄弟元素、" ~ " 之後的所有兄弟 元素

    Advertisements

選擇器表達式由塊表達式和塊間關係符組成,如圖 3-2 所示。其中,塊表達式分為 3 種: 簡單表達式、屬性表達式、偽類表達式;塊間關係符分為 4 種:">" 父子關係、"" 祖先後 代關係、"+" 緊挨著的兄弟元素、" ~ " 之後的所有兄弟元素;塊表達式和塊間關係符組成 了層級表達式。

設計思路

分析下如果要執行一段選擇器表達式,或者說設計一個簡化版的選擇器引擎,需要做些什麼工作。下面以 "div.red>p" 為例來 模擬執行過程,具體來說有從左向右查找和從右向左查找兩種思路:

1)從左向右:先查找 "div.red" 匹配的元素集合,然後查找匹配 "p" 的子元素集合。

2)從右向左:先查找 "p" 匹配的元素集合,然後檢查其中每個元素的父元素是否匹配 "div.red"。

無論是從左向右還是從右向左,都必須經歷下面 3 個步驟:

1)首先要能正確地解析出 "div.red>p" 中的 "div.red"、"p" 和 ">",即解析出選擇器 表達式中的塊表達式和塊間關係符。這一步是必需的,否則根本無從下手。

2)然後要能正確地找到與 "div.red" 或 "p" 匹配的元素集合,即查找單個塊表達式的匹 配元素集合。以 "div.red" 為例,可以有兩種實現方式:

a. 先查找匹配 "div" 的元素集合,然後從中過濾出匹配 ".red" 的元素集合。

b. 先查找匹配 ".red" 的元素集合,然後從中過濾出匹配 "div" 的元素集合。 不管採用以上哪種方式,這個過程都可以分解為兩個步驟:第一步用塊表達式的一部分

進行查找,第二步用塊表達式的剩餘部分對查找的結果進行過濾。

3)最後來處理 "div.red" 和 "p" 之間的關係符 ">",即處理塊表達式之間的父子關係。

在這一步驟中,從左向右和從右向左的處理方式是截然不同的:

a. 從左向右:找到 "div.red" 匹配的元素集合的子元素集合,然後從中過濾出匹配 "p"

的子元素集合。

b. 從右向左:檢查每個匹配 "p" 的元素的父元素是否匹配 "div.red",只保留匹配的元素。 無論採用以上哪種方式,這個過程都可以分解為兩個步驟:第一步按照塊間關係符查找

元素,第二步用塊表達式對查找的結果進行過濾。不論元素之間是哪種關係(父子關係、祖 先後代關係、相鄰的兄弟關係或不相鄰的兄弟關係),都可以採用這種方式來查找和過濾。

另外,如果還有更多的塊表達式,則重複執行第 3 步。

對於前面的 3 個步驟,可以進一步提煉總結,如下:

1)處理選擇器表達式:解析選擇器表達式中的塊表達式和塊間關係符。

2)處理塊表達式:用塊表達式的一部分查找,用剩餘部分對查找結果進行過濾。

3)處理塊間關係符:按照塊間關係符查找,用塊表達式對查找結果進行過濾。

從前面對選擇器表達式的執行過程的分析,還可以推導分析出以下結論: ‰ 從左向右的總體思路是不斷縮小上下文,即不斷縮小查找範圍。 ‰ 從右向左的總體思路是先查找後過濾。 ‰ 在從左向右的查找過程中,每次處理塊間關係符時都需要處理未知數量的子元素或後代元素,而在從右向左的查找過程中,處理塊間關係符時只需要處理單個父元素或有 限數量的祖先元素。因此,在大多數情況下,採用從右向左的查找方式其效果要高於 從左向右。

在了解了兩種執行思路后,現在再來看看 Sizzle,它是一款從右向左查找的選擇器引擎, 提供了與前面 3 個步驟相對應的核心介面:

  • ‰ 正則 chunker 負責從選擇器表達式中提取塊表達式和塊間關係符。

  • ‰ 方法 Sizzle.find( expr, context, isXML ) 負責查找塊表達式匹配的元素集合,方法Sizzle.filter( expr, set, inplace, not ) 負責用塊表達式過濾元素集合。

  • ‰ 對象 Sizzle.selector.relative 中的塊間關係過濾函數根據塊間關係符過濾元素集合。 函數 Sizzle( selector, context, results, seed ) 則按照前面 3 個步驟將這些核心介面組織起來。

Sizzle( selector, context, results, seed )

函數 Sizzle( selector, context, results, seed ) 用於查找與選擇器表達式 selector 匹配的元素 集合。該函數是選擇器引擎的入口。

函數 Sizzle( selector, context, results, seed ) 執行的 6 個關鍵步驟如下:

1)解析塊表達式和塊間關係符。

2)如果存在位置偽類,則從左向右查找:

a. 查找第一個塊表達式匹配的元素集合,得到第一個上下文元素集合。

b. 遍歷剩餘的塊表達式和塊間關係符,不斷縮小上下文元素集合。

3)否則從右向左查找:

a. 查找最後一個塊表達式匹配的元素集合,得到候選集、映射集。

b. 遍歷剩餘的塊表達式和塊間關係符,對映射集執行塊間關係過濾。

4)根據映射集篩選候選集,將最終匹配的元素放入結果集。

5)如果存在並列選擇器表達式,則遞歸調用 Sizzle( selector, context, results, seed ) 查找匹配的元素集合,併合並、排序、去重。

6)最後返回結果集。


你都看到這了?!不點個關注,加個收藏再走嘛~

防止閱讀疲勞,歡迎大家持續關注。號內有多個專題,小程序(持續更新中),Javascript(完結),Vue(視頻),JQuery(持續更新)等學習資源。覺得有收穫的可以收藏關注,歡迎騷擾,一起學習,共同進步。


我的小程序,自律更自由,如果你也喜歡鍛煉的話在這裡尋找你的小夥伴吧。

一隻喜歡鍛煉的程序猿,嘿嘿。

Advertisements

你可能會喜歡