共用方式為


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

邊做邊學 jQuery 系列 4 - 神奇的 jQuery Selector 教學影片

> [!VIDEO https://www.microsoft.com/zh-tw/videoplayer/embed/7829b078-05f9-45f0-83de-10d42e061d87]

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 是如何迷倒一票網頁程式設計師的。

【範例檔案下載】