邊做邊學jQuery系列04 - 神奇的jQuery Selector

課程錄製時間: 2009年
神奇的jQuery Selector

Selector是jQuery的精髓所在,也是讓眾多開發人員深深著迷之處,要體會jQuery的好,就要先從Selector下手。

傳統DOM API提供我們幾種找到特定元素的方法,如: getElementById()、getElementsByName()、getElementsByTagName()... 等等,若要由相對從屬關係去找,則要由childNodes()、parentNode()下手。遇到複雜一點的需求,例如要找出所有被包在div中target="_blank"的<a>,以不變應萬變的寫法是先用getElementsByTag("div")取得所有<div>的陣列,跑迴圈針對這些<div>再以getElementsByTagName("a")取回其下<a>的陣列,再跑迴圈一一比對target屬性是否為"_blank",十分麻煩。

在CSS中,有所謂Selector的概念,大家應該都看過以下寫法:

table a { color: red }
#myTable .clsInput { width: 60px; }

以上語法可以一口氣將所有被包在<table>中的<a>改為藍色,將<table id="myTable">下class="clsInput"的元素寬度設為60px,是不是比起DOM API的愚公移山先進許多,在CSS的新一代標準裡,甚至我們可以寫成 div a[target="_blank"] 。咦? 方括號[ ]包了像是XPATH的屬性條件設定,指定了target要等於_blank,一行語法當場抵掉剛才的兩層Javascript迴圈,顯然是更有效率的玩法。

jQuery Selector很重要的精神便在於企圖在Javascript裡實踐CSS3 Selector的效果,一方面神奇地簡化了取得元素的過程,再者,網頁設計人員多少有點CSS Selector的基礎,因此不需額外學習就能在jQuery中輕易上手。

在jQuery裡,Selector尋找元素可由幾個方面著手:

  1. 指定標籤(Tag)類別
    例如: $("a")就是找出頁面所有<a>的集合
  2. 指定識別代號(id)
    例如: $("#myTable")會找出<table id="myTable">
  3. 指定CSS類別名稱
    例如: $(".clsInput")找出所有class屬性中包含clsInput的元素,注意是"包含",一個元素可以同時指定套用多個CSS類別。
  4. 額外條件
    jQuery支援一堆以冒號起始的補充條件,例如$("#myTable tr:odd")表示只選取單數列、$("input:radio")選取所有的<input type="radio">

以上的這些條件值是可以組合應用的,例如: $("div.clsPublic table a:first")是找出所有CSS類別為clsPublic的<div>下的<table>中第一個出現的<a>,充分組合運用,就可以產生用一行Selector取代數十行迴圈搜尋的神奇效果。

以下整理常用的Selector:

  • * 所有元素
  • E 所有E元素,
  • E:nth-child(n) 傳回在所屬父元素下,排行第n的元素E。(注意: 從1開始算,而非從0)
  • E:first-child 傳回在所屬父元素下,排行第一的元素者(老大)
  • E:last-child 傳回在所屬父元素下,排行最後的元素者(老么)
  • E:only-child 傳回在所屬父元素下,是唯一子元素者(獨子)
  • E:empty 沒有任何子元素者(有文字也不算,例如<span>TEXT</span>就不合格)
  • E:enabled 未被停用的UI元素E
  • E:disabled 被停用的UI元素E
  • E:checked 被勾選的UI元素E(適用於Radio, Checkbox)
  • E:selected 被選取的UI元素E(適用於Select下的Option元素)
  • E.myClassName CSS類別設為myClassName的元素E
  • E#myId id="myId"的元素E
  • E:not(s) 不符合s條件的元素E,例如: $("span:not(:empty)")即為找出有包含子元素的span
  • E F 由包含在E底下的F元素,不必直接為父子關係,例如: F可以是E的子元素的子元素。
  • E > F 找出父元素為E的F元素
  • E + F 找出緊接在E元素後方的F元素
  • E ~ F 找出緊接在E元素前方的F元素
  • E,F,G 利用逗號可以呈現"或"的聯集效果,指E元素或F元素或G元素
  • E[foo] 具有foo屬性(Attribute)的E元素(1.2.6以前的版本亦可用E[@foo]表示,1.3版本起取消@符號的使用)
  • E[foo=bar] 有foo屬性,且foo屬性值為bar的E元素
  • E[foo!=bar] 有foo屬性,且foo屬性值不等於bar的E元素
  • E[foo^=bar] 有foo屬性,且foo屬性值以bar開頭的E元素
  • E[foo$=bar] 有foo屬性,且foo屬性值以bar結尾的E元素
  • E[foo*=bar] 有foo屬性,且foo屬性值中包含bar字眼的E元素
  • E[foo=bar][baz=bop]  同時具備foo及baz屬性,且其值分別為bar及bop的E元素
  • :even 由選取結果中只挑出第雙數個
  • :odd 由選取結果中只挑出第單數個
  • :eq(N) and :nth(N) 由選取結果中取出第N個,由0起算
  • :gt(N) 由選取結果中只保留第N個以後者(不含N)
  • :lt(N) 由選取結果中只保留第N個以前者(不含N)
  • :first 選取結果中的第一個,等同於:eq(0)
  • :last 選取結果中的最後一個
  • :parent 選取包含子元素者(文字內容也算,例如:<span>Text</span>)
  • :contains('test') 選取元素內的文字包含特定字串者
  • :visible 選取所有可見的元素(包含CSS display=block或inline, visibility=visible但不含<input type="hidden">)
  • :hidden 選取所有不可見的元素(包含CSS display=none, visibility=hidden以及<input type="hidden">) 
  • :input 選取所有表單輸入元素(包含input, select, textarea, button).
  • :text 選取所有<input type="text">
  • :password 選取所有<input type="password ">
  • :radio 選取所有<input type="radio ">
  • :checkbox 選取所有<input type="checkbox">
  • :submit 選取所有<input type="submit">
  • :image 選取所有<input type="image">
  • :reset 選取所有<input type="reset">
  • :button 選取所有<input type="button">
  • :file 選取所有<input type="file">

以上提的這些選擇條件可以組合應用,發揮驚人的彈性,例如: $("input.clsName:hidden[value=AAA]")可以找出所有不可見,CSS類別為clsName,且值為AAA的input元素,一行Selector到位,省卻原本要用for迴圈加上一大堆if才能完成的篩選邏輯。

由於jQuery Selector傳回的結果常是一群符合元素jQuery物件所組成的群組,還有有幾個取得群組特定元素的函數:

  • size()length 傳回群組的物件個數
  • eq(N) 取出群組中第N個jQuery物件
  • get() 傳回元素的陣列
  • get(N) 取出第N個元素
  • index(element或jQuery物件) 用來找某元素在選取結果中的排名順序,例如網頁上有五個<div>,<div id="dvX">排名第三個,則$("div").index(document.getElementById("dvX"))可以得到2(由0起算,故第三個為2),$("div").index($("#dvX"))也可得到同樣結果。元素不在群組時傳回-1。

剛才看過$("input").css("color", "red")可以對每一個<input>進行CSS顏色設定,但如果要進行的操作是由我們自訂的邏輯來處理呢? 有個好用的函數.each()可以派上用場。例如我們要一一alert所有<input>的值,寫法如下:

$("input").each(function() { alert(this.value); })  $("input").each(function() { alert($(this).val()); })

這裡用了上次提過的匿名函數宣告,而這個函數會依選取結果的元素個數被呼叫多次,而每次被呼叫時,this便是該次輪到的元素。this是HTML元素而非jQuery物件,所以上例中示範了用this.value直接透過DOM取值或再包成jQuery物件用val()取值兩種寫法。如果我們還需要知道該元素在結果群組中的排名,則可以寫成:

$("input").each(function(i) { alert(i + "=>" + this.value); });

另外,我們也可以事後再增減選取的群組,例如: $("input").add("select")

最後來個練習,手工做一個5X5的Table當作空白棋盤,要塗成右方的間隔黑底。

相信嗎? 程式碼只要一行: $("#myTbl td:even").css("background-color", "black");

接著我們在其中依序每個格子填上1,2,3

利用.each(),這項工作一點也不困難:

$("#myTbl td:even").css({ backgroundColor: "black", color: "white" });
$("#myTbl td").each(function(i) { $(this).text((i % 3) + 1); });

最後,找出所有填入數字為3者,將文字顏色改為紅色:

一樣只需一行解決: $("#myTbl td:contains('3')").css("color", "red");

接著,更奇妙的部分來了! 如果你對現有Selector的篩選邏輯不滿意,jQuery也開放讓你自訂Selector,很酷吧? 這裡我們就借用以上的例子,自己DIY一個"文字內容為3"的古怪Selector。

自訂Selector的設定語法為 $.extend($.expr[":"], { filterName: filterEvalExpression }); 其中filterName是要自訂的Selector Filter名稱,filterEvalExpression的字串內容則是一個邏輯運算式,用以傳回true或false,而在運算式中,我們有幾個預設變數可用:

  • a 比對的DOM元素物件
  • i 元素在查詢結果的排序位置
  • m 拆解Selector而成的字串陣列,當Filter是:filterName('aaa')時,可利用m[3]取得aaa,作為比對之用。

有興趣深入研究的人可以參考jquery.js裡的Filter寫法,應該很快就能照著研發出自己想要的Selector。 "文字內容包含3"的古怪Selector則可以寫成:

$.extend($.expr[":"], { textIsThree: function(a, i, m) { return (a.textContent||a.innerText||jQuery(a).text()||'') == '3'); } });
$("#myTbl td:textIsThree").css("color", "red");

由以上的範例,不難突顯"善用Selector,可以少寫許多Code"的事實,相信大家已經漸漸體會出jQuery是如何迷倒一票網頁程式設計師的。

【範例檔案下載】

歡迎推文分享:



 
RSS
【工商服務】
OrcsWeb: Windows Server Hosting
twMVC

關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。