看板 Ajax 關於我們 聯絡資訊
也可以參考 http://ithelp.ithome.com.tw/question/10090810 ----------------------------------- ----------------------------------- 基本上 selector 嚴格分類來講有相當多種, slibing / parent ...etc 可以看這裡有分類 http://api.jquery.com/category/selectors/ CSS 提供給我們的那些相信大家都不陌生, ID(#)/Tag/class(.)/attr([])/child(>) ...etc 其中這次我要介紹的是, 一些算是比較不屬於 css3 規範的異類 selector , 大家可能常用(至少我常用),但是不見得知道他的運作模式, 這裡我們就是要來告訴你他是什麼。:) jQuery 的 selector 其實暗藏很多我們不知道的玄機, 我會盡量再多介紹一些。 ------- ok 不賣關子,我們開始談主題。 大家有沒有用過 pseudo selector ,像是這個範例? ----------------------------------- <div class="test"></div> <div class="test" style="display:none"></div> <div class="test" style="display:none"></div> <script> alert($(".test:visible").length); </script> ----------------------------------- Runnable sample : http://jsfiddle.net/GGRD6/ ":visible" selector,我相信大家都知道這不是 css 規格,但是它會動。:) 這種冒號開頭的選擇器,jQuery 稱之為虛擬選擇器。 (其實還再細分出一種是 pos selector 如 :eq / :lt / :odd ..etc ,剩下的都稱為 pseudo selector, 但這裡我先統稱為 pseudo selector 以便說明,原理是相近的。) 這時候我們就會需要來看看 jQuery 官方文件: http://api.jquery.com/visible-selector/ ----------------------------------- Because :visible is a jQuery extension and not part of the CSS specification, queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(":visible"). ----------------------------------- (不負責翻譯) ----------------------------------- 因為 :visible 是一個 jQuery extension 而非 CSS 規格,使用 :visible 查詢時, 將無法利用到原生的 querySelectorAll() 函式,會比較慢。 要獲得最好效能,建議是先使用純 css 查詢再使用 filter 進行過濾。 ----------------------------------- 重要的是這裡印證虛擬選擇器可能都不是 css 官方規格, 這裡他同時說兩件我們需要知道的事情,但效能部份我們先不管, 我們文末會回頭再談,我們先介紹虛擬選擇器的原理。 為了說明,我想 :visible 一個例子是不夠的, 我再舉另一個例子 ":contains()" 好了, 這也是相當常見的例子,尋找指定元素內有包含特定字串的。 ----------------------------------- <div> <div class="test" style="display:none">I am a div </div> <div class="test" style="display:none"> I am also div </div> <div class="test" style="display:none">I am still a div </div> </div> <script> $(document.body).append(".test:contains('div') #=> "+ $(".test:contains('div')").length +"<br />"); $(document.body).append(".test:contains('still') #=> "+ $(".test:contains('still')").length +"<br />"); </script> ----------------------------------- 將會輸出 .test:contains('div') #=> 3 .test:contains('still') #=> 1 runnable sample http://jsfiddle.net/GGRD6/1/ 官方文件 http://api.jquery.com/contains-selector/ ---- 當然還有 :not 這個一定是不可少的。 ----------------------------------- <div> <div class="test test1" style="display:none">I am a div </div> <div class="test test2" style="display:none"> I am also div </div> <div class="test test3" style="display:none">I am still a div </div> </div> <script> $(document.body).append(".test:not('.test1') #=> "+ $(".test:not('.test1')").length +"<br />"); $(document.body).append(".test:not('.test1,.test2') #=> "+ $(".test:not('.test1,.test2')").length +"<br />"); </script> ----------------------------------- 輸出 .test:not('.test1') #=> 2 .test:not('.test1,.test2') #=> 1 官方文件 http://api.jquery.com/not-selector/ ---------------------------- 你一定很好奇為什麼我要特別介紹這個,我可以一個一個作 selector 的介紹, 為什麼要介紹「pseudo selector 」,他有什麼好介紹的? 除了效能議題以外,其實我更重視的是他實作的邏輯, 我曾經很好奇 jQuery 是怎麼實作的而翻過很多次他的原始碼, 而其中這一個是我特別有印象的。:) 他有趣的地方在於一是實作跟虛擬選擇器的對應,另外就是他的可擴充性。 以我們 JS developer 來講, 碰到 selelector 這種帶著規則的「字串」東西其實蠻沒轍的, 他又不是 call 一個純 js function , 你如果想 trace 也要多繞好幾圈才看的懂。 而 pseudo selector 的條件定義呢, 就由 jQuery.expr.filters 跟 jQuery.expr.setFilters (for pos selectors) 掌管, 所以你可以翻原始碼,也可以用這種方式找出所有虛擬選擇器: ----------------------------------- var filters = jQuery.expr.filters; for(var i in filters ){ $(document.body).append(i+" #=> "+ filters[i].toString()+"<br /><br />"); } $(document.body).append( "<div style='color:red;'>setFilters </div><br />"); filters = jQuery.expr.setFilters; for(var i in filters ){ $(document.body).append(i+" #=> "+ filters [i].toString()+"<br /><br />"); } ----------------------------------- 因為 details 有點多,想看細節的實作的可以移步 XD http://jsfiddle.net/yAAqD/ 翻翻 jQuery 1.7.2 的 4565 ~ 4686 行程式碼~(這只是其中部份。) 你會發現他基本上流程是這麼作: 1.先檢查是不是 pos selector ,是的話,就跑 jQuery.expr.setFilters 出來作 filter。 (:nth , :eq, :gt, :lt, :first, :last, :even, :odd ) 2.如果不是,就找 jQuery.expr.filters 如果後面還有跟其他 selector ,會再套疊後續的 operator 去做查詢。 --- 你可能會好奇,如果我給 $(".test:myfilter") 會發生什麼事情,答案是會噴 error: Syntax error, unrecognized expression: myfilter --- 所以理論上要擴充一個新的 filter , 直接擴充 jQuery.expr.filters 就可以了。 (如果你要擴充 set selector ,要改比較多東西,有興趣再問吧XD) 舉個例子,我今天比較機車,我想留 id 裡面, 有包含 tony 的元素,我可以這樣實作。 ----------------------------------- jQuery.expr.filters.isTony = function(elem){ return elem.id && elem.id.indexOf("tony") != -1; } //query with the pseudo selector //$("div:isTony") ----------------------------------- Sample http://jsfiddle.net/LMBgN/ 至於效能,基本上就如官網文件所說。 基本上 "查詢" ($(selector) 或 $parent.find(selector)) , 能不用虛擬 selector 就不該用; 而"過濾" ($set.filter(selector) or $set.is(selector )) 就很適合用。:) ----------------------------------- 文末的文末,補個題外話,我一直很訝異的是, jQuery mobile 是用這麼髒的方式, 來實作他們需要的內部用 pseudo selector。:P 有時候看看別人的實作,想想背後的理由,也是蠻有意思的。:P ----------------------------------- // Monkey-patching Sizzle to filter the :jqmData selector var oldFind = $.find, jqmDataRE = /:jqmData\(([^)]*)\)/g; $.find = function( selector, context, ret, extra ) { selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); return oldFind.call( this, selector, context, ret, extra ); }; ----------------------------------- 這篇因為想睡了講的比較亂,有興趣/疑問歡迎發問。:) -- Life's a struggle but beautiful. -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 114.25.56.138 ※ 編輯: TonyQ 來自: 114.25.56.138 (04/16 00:57) ※ 編輯: TonyQ 來自: 114.25.56.138 (04/16 00:57)
coldollsheep:不錯喔 04/16 10:43
davidsky:UI.widget有實作widget的pseudo selector如':ui-tabs' 04/17 00:52
davidsky:有段時間一直在用這個來快速找到widget...後來發現真的 04/17 00:53
davidsky:很慢XD 全部換掉 04/17 00:53
davidsky:但是有這功能出發點是不錯的 04/17 00:53
TonyQ:我猜這就是為什麼 jquery mobile 這樣作 04/17 01:49
TonyQ:因為它走回 attr selector 還是可以用到 qsa 04/17 01:49