jQuery有幾個重要的組成部分,其中一個重要部份就是selector,讓你可以利用CSS3 selector的語法快速找到目標的dom node,然後加以操作。
selector
selector其實是從CSS規格中來的東西,原本與Javascript不太有關係,主要是應用在style sheet裡面用來指定某些dom node的style。但是也因為CSS用這樣的語法,讓處理dom node的方式有了更多的可能性。
一些native method
DOM3規格中定義了幾個取得node的方法:
* document.getElementById()
* document.getElementsByTagName()
另外在DOM2HTML規格中額外定義了一個取得node的方法:
* document.getElementsByName()
所以其實在網頁開發時,直接可用的只有這幾個方法。另外有幾個Collection:document.images、document.applets、document.links、document.forms、document.anchors等,不過anchors、images是為了相容性設計的,並不可靠。所以有許多人會建議避開使用這些collections。
jQuery的實作
那jQuery是如何實作出selector呢?
其實jQuery1.3之後,使用了一個新的selector引擎,叫做Sizzle JS。如果仔細對照一下jQuery以及sizzle的程式就會發現,jQuery其實只是把sizzle原封不動搬進來用。(不過sizzle也是John Resig開發的...這傢伙真厲害)(還是用jquery-1.3.2.js來觀察,處理selector的部份在1419~2427行)
這麼長的程式碼,要仔細分析每個部份的功能的話,大概就寫不完了...所以還是提綱契領地分析。先看最後expose給jQuery的API:(2363~2367行)
// EXPOSE
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
這裡看到了jQuery.find(),其實就是Sizzle(),從前面的程式碼也可以知道,在許多狀況下,$()會透過jQuery.find()來處理傳進來的selector,返回找到的dom node陣列。所以Sizzle()就是最初的進入點,其他三個部份主要是做過濾還有剖析的函數,用來判斷是id、class name、html tag、attribute等的pattern以及適用上述pattern matching的順序。在Sizzle函數一開始有一個叫做chunker的Regular Expression,Sizzle利用他來把selector字串剖析成token,然後根據token的內容來呼叫expr及filter函數來取得dom node。這種處理方式類似一個小型的compiler了。
除了主要的架構,Sizzle還用了許多方法針對各個瀏覽器在處理過程會出現的不同結果做個別處理,來讓使用的方法在所有瀏覽器都一體適用。另外還有一些功能是偵測瀏覽器是否內建一些額外的取得dom node的方法,如果有的話就使用這些方法來加速處理。
接下來回頭看jQuery.fn.init(),裡面對傳進來的參數做了一些判斷,如果傳進來的是dom node,那就把這個dom node加到this[0],然後返回this。如果傳進來的是一個function,他就會變成呼叫jQuery(document).ready(function)。如果傳進來的是一個#id,那就呼叫document.getElementById來取得這個node,否則就呼叫find()函數透過Sizzle做處理。另外,在Sizzle裡面,如果selector字串開頭是#id,也會先呼叫document.getElementById()。所以在一些調校jQuery的文章中,都會建議最好先用#id這個selector,因為這樣速度最快,而且所有瀏覽器都支援。如果真的要使用Sizzle而完全不用瀏覽器內建的取得dom node方法的話,速度其實會差非常多,只是在瀏覽器中Javascript跑得也相當快,所以往往感受不到速度差距。(當然,如果在迴圈裡做就...)
使用
關於Sizzle的使用,可以到http://wiki.github.com/jeresig/sizzle去看一下使用說明,不過在jQuery中需要改成上文中提到他expose出來的API名稱。不過要注意的是,像jQuery.find()回傳的是node陣列,而不是包裝好的jQuery物件,所以不能直接串接使用。另外需要擴充他的expression的話,可以透過jQuery.expr來加入規則。想檢視他的運作時,也可以在這些expression中插入一些檢查點。