神奇的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尋找元素可由幾個方面著手:
-
指定標籤(Tag)類別
例如: $("a")就是找出頁面所有<a>的集合
-
指定識別代號(id)
例如: $("#myTable")會找出<table id="myTable">
-
指定CSS類別名稱
例如: $(".clsInput")找出所有class屬性中包含clsInput的元素,注意是"包含",一個元素可以同時指定套用多個CSS類別。
-
額外條件
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是如何迷倒一票網頁程式設計師的。
【範例檔案下載】