<b>3.8 sizzle.selectors.relative</b>
對象sizzle.selectors.relative中存放了塊間關系符和對應的塊間關系過濾函數,稱為“塊間關系過濾函數集”。
塊間關系符共有4種,其含義和過濾方式如表3-2所示。
圖3-6 sizzle.filter( expr, set, inplace, not )的執行過程
表3-2 塊間關系符的含義和過濾方式
序号 塊間關系符 選擇器表達式 說 明 從右向左的過濾方式
1 "" ancestor descendant 比對所有後代元素 檢查祖先元素是否比對左側的塊表達式
2 "+" prev + next 比對下一個兄弟元素 檢查前一個兄弟元素否比對左側的塊表達式
3 ">" parent > child 比對所有子元素 檢查父元素是否比對左側的塊表達式
4 "~" prev~siblings 比對之後的所有兄弟元素 檢查之前的兄弟元素是否比對左側的塊表達式
在函數sizzle( selector, context, results, seed )從右向左進行過濾時,塊間關系過濾函數被調用,用于檢查映射集checkset中的元素是否比對塊間關系符左側的塊表達式。調用時的參數格式為:
sizzle.selectors.relative[ 塊間關系符 cur ]( 映射集 checkset,
左側塊表達式 pop, contextxml );
塊間關系過濾函數接受3個參數:
參數checkset:映射集,對該元素集合執行過濾操作。
參數part:大多數情況下是塊間關系符左側的塊表達式,該參數也可以是dom元素。
參數isxml:布爾值,訓示是否運作在一個xml文檔中。
塊間關系過濾函數實作的3個關鍵步驟如下:
1)周遊映射集checkset。
2)按照塊間關系符查找每個元素的兄弟元素、父元素或祖先元素。
3)檢查找到的元素是否比對參數part,并替換映射集checkset中對應位置的元素。
a.?如果參數part是标簽,則檢查找到的元素其節點名稱nodename是否與之相等,如果相等則替換為找到的元素,不相等則替換為false。
b.?如果參數part是dom元素,則檢查找到的元素是否與之相等,如果相等則替換為true,不相等則替換為false。
c.?如果參數part是非标簽字元串,則調用方法sizzle.filter(
selector, set, inplace, not )過濾。
也就是說,周遊結束後,映射集checkset中的元素可能會是兄弟元素、父元素、祖先元素、true或false。
3.8.1 "+"
塊間關系符"+"比對選擇器"prev + next",即比對所有緊接在元素prev後的兄弟元素next。例如,$("div
+ span")、$(".lastdiv + span")。對于從右向左的查找方式,則是檢查元素next之前的兄弟元素是否比對塊表達式prev。
相關代碼如下所示:
4221 var expr =
sizzle.selectors = {
4251
relative: {
4252
"+": function(checkset, part){
4253 var ispartstr = typeof part ===
"string",
4254 istag = ispartstr &&
!rnonword.test( part ),
4255 ispartstrnottag = ispartstr
&& !istag;
4256
4257 if ( istag ) {
4258 part = part.tolowercase();
4259 }
4260
4261 for ( var i = 0, l =
checkset.length, elem; i < l; i++ ) {
4262 if ( (elem = checkset[i]) ) {
4263 while ( (elem = elem.previoussibling)
&& elem.nodetype !== 1 ) {}
4264
4265 checkset[i] =
ispartstrnottag || elem && elem.node
name.tolowercase() === part ?
4266 elem || false :
4267 elem === part;
4268 }
4269 }
4270
4271 if ( ispartstrnottag ) {
4272 sizzle.filter( part, checkset,
true );
4273 }
4274
},
4338
4749 };
第4253~4255行:定義一組局部變量,它們的含義和用途如下:
變量ispartstr:訓示參數part是否是字元串。
變量istag:訓示參數part是否為标簽字元串。
變量ispartstrnottag:訓示參數part是否是非标簽字元串。
第4261~4269行:周遊映射集checkset,查找每個元素的前一個兄弟元素,并替換映射集checkset中對應位置的元素,有以下3個邏輯分支:
如果未找到兄弟元素,則替換為false。
如果找到了兄弟元素,并且參數part是标簽,則檢查兄弟元素的節點名稱nodename是否與之相等,如果相等則替換為兄弟元素,不相等則替換為false。
如果找到了兄弟元素,并且參數part是dom元素,則檢查二者是否相等,如果相等則替換為true,不相等則替換為false。
是以,在周遊結束後,映射集checkset中的元素可能會是兄弟元素、true或false。
第4263行:在周遊兄弟元素的同時過濾掉非元素節點,并且隻要取到一個兄弟元素就退出while循環。
第4271~4273行:如果參數part是非标簽字元串,則調用方法sizzle.filter( selector, set, inplace, not )過濾映射集checkset。對于參數part是标簽和dom元素的情況,在前面周遊映射集checkset時已經處理過了。
3.8.2 ">"
塊間關系符">"用于選擇器"parent > child",即比對父元素parent下的子元素child。例如,$("div
+ span")、$(".lastdiv + span")。對于從右向左的查找方式,則是檢查子元素child的父元素是否比對塊表達式parent。
4276
">": function( checkset, part ) {
4277 var elem,
4278 ispartstr = typeof part ===
4279 i = 0,
4280 l = checkset.length;
4281
4282 if ( ispartstr &&
!rnonword.test( part ) ) {
4283 part = part.tolowercase();
4284
4285 for ( ; i < l; i++ ) {
4286 elem = checkset[i];
4287
4288 if ( elem ) {
4289 var parent =
elem.parentnode;
4290 checkset[i] =
parent.nodename.tolowercase() === part ? parent : false;
4291 }
4292 }
4293
4294 } else {
4295 for ( ; i < l; i++ ) {
4296 elem = checkset[i];
4297
4298 if ( elem ) {
4299 checkset[i] =
ispartstr ?
4300 elem.parentnode :
4301 elem.parentnode ===
part;
4302 }
4303
}
4304
4305 if ( ispartstr ) {
4306 sizzle.filter( part,
checkset, true );
4307 }
4308 }
4309
第4282~4292行:如果參數part是标簽,則周遊映射集checkset,查找每個元素的父元素,并檢查父元素的節點名稱nodename是否與參數part相等,如果相等則替換映射集checkset中對應位置的元素為父元素,不相等則替換為false。
第4294~4307行:如果參數part不是标簽,則可能是非标簽字元串或dom元素,同樣周遊映射集checkset,查找每個元素的父元素,并替換映射集checkset中對應位置的元素,在這個過程中有以下2個邏輯分支:
如果參數part是非标簽字元串,則在周遊映射集checkset的過程中,替換映射集checkset中對應位置的元素為父元素,周遊結束後調用方法sizzle.filter( selector, set, inplace, not )過濾映射集checkset。
如果參數part是元素,則在周遊映射集checkset時,檢查每個元素的父元素是否與之相等,如果相等則替換映射集checkset中對應位置的元素為true,不相等則替換為false。
是以,在周遊結束後,映射集checkset中的元素可能會是父親元素、true或false。
3.8.3 ""
塊間關系符""用于選擇器"ancestor descendant",即比對祖先元素ancestor的所有後代元素descendant。例如,$("div
button")、$("div .btn")。對于從右向左的查找方式,則是檢查後代元素descendant的祖先元素是否比對塊表達式ancestor。
4311
"": function(checkset, part, isxml){
4312 var nodecheck,
4313 donename = done++,
4314 checkfn = dircheck;
4315
4316 if ( typeof part ===
"string" && !rnonword.test( part ) ) {
4317 part = part.tolowercase();
4318 nodecheck = part;
4319 checkfn = dirnodecheck;
4320 }
4321
4322 checkfn( "parentnode",
part, donename, checkset, nodecheck, isxml );
4323
第4312~4322行:這段代碼含有2個邏輯分支:
如果參數part是非标簽字元串或dom元素,則調用函數dircheck()過濾映射集checkset。
如果參數part是标簽,則調用函數dirnodecheck()過濾映射集checkset。
調用函數dircheck()和dirnodecheck()時的參數格式為:
checkfn( 方向
"parentnode/previoussibling", 塊表達式 part, 緩存計數器
donename, 映射集 checkset, nodecheck, isxml )
函數dircheck()和dirnodecheck()會周遊映射集checkset,查找每個元素的祖先元素,并檢查是否有祖先元素比對參數part,同時替換映射集checkset中對應位置的元素。具體請參見3.8.5節和3.8.6節。
3.8.4 "~"
塊間關系符"~"用于選擇器"prev~siblings",即比對元素prev之後的所有兄弟元素siblings。例如,$('div~p')。對于從右向左的查找方式,則是檢查元素siblings之前的兄弟元素是否比對塊表達式prev。
sizzle.selectors.relative["~"](
checkset, part )的源碼實作與sizzle.selectors.relative[""]( checkset, part )幾乎一樣,兩者的差別僅僅在于調用函數dircheck()和dirnodecheck()時第一個參數的值不同,前者是"previoussibling",後者則是"parentnode"。
4221 var expr = sizzle.selectors = {
4325
"~": function( checkset, part, isxml ) {
4326 var nodecheck,
4327 donename = done++,
4328 checkfn = dircheck;
4329
4330 if ( typeof part ===
4331 part = part.tolowercase();
4332 nodecheck = part;
4333 checkfn = dirnodecheck;
4334 }
4335
4336 checkfn(
"previoussibling", part, donename, checkset, nodecheck, isxml );
4337
}
3.8.5 dircheck( dir, cur,
donename, checkset, nodecheck, isxml )
函數dircheck( dir, cur, donename, checkset, nodecheck, isxml )負責周遊候選集checkset,檢查其中每個元素在某個方向dir上是否有與參數cur比對或相等的元素。如果找到,則将候選集checkset中對應位置的元素替換為找到的元素或true;如果未找到,則替換為false。
在塊間關系過濾函數sizzle.selectors.relative[""/"~"](
checkset, part )中,當參數part是非标簽字元串或dom元素時,才會調用函數dircheck()。
5201 function dircheck( dir, cur, donename,
checkset, nodecheck, isxml ) {
5202
for ( var i = 0, l = checkset.length; i < l; i++ ) {
5203
var elem = checkset[i];
5204
5205
if ( elem ) {
5206 var match = false;
5207
5208 elem = elem[dir];
5209
5210 while ( elem ) {
5211 if ( elem[ expando ] ===
donename ) {
5212 match = checkset[elem.sizset];
5213 break;
5214 }
5215
5216 if ( elem.nodetype === 1 ) {
5217 if ( !isxml ) {
5218 elem[ expando ] =
donename;
5219 elem.sizset = i;
5220 }
5221
5222 if ( typeof cur !==
"string" ) {
5223 if ( elem === cur ) {
5224 match = true;
5225 break;
5226
}
5227
5228 } else if ( sizzle.filter(
cur, [elem] ).length > 0 ) {
5229 match = elem;
5230 break;
5231 }
5232 }
5233
5234 elem = elem[dir];
5235 }
5236
5237 checkset[i] = match;
5238
5239
5240 }
第5201行:定義函數dircheck( dir, cur, donename, checkset, nodecheck, isxml ),它接受6個參數:
參數dir:表示查找方向的字元串,例如,“parentnode”、“previoussibling”。
參數cur:大多數情況下是非标簽字元串格式的塊表達式,也可能是dom元素。
參數donename:數值。本次查找的唯一辨別,用于優化查找過程,避免重複查找。
參數checkset:候選集,在查找過程中,其中的元素将被替換為父元素、祖先元素、兄弟元素、true或false。
參數nodecheck:undefined。在後面的代碼中沒有用到該參數。
第5202~5239行:周遊候選集checkset,對其中的每個元素,沿着某個方向(例如,“parentnode”、“previoussibling”)一直查找,直到找到與參數cur(dom元素)相等或者與參數cur(非标簽字元串)比對的元素為止,或者直到在該方向上不再有元素為止。
如果找到與參數cur(dom元素)相等的元素,則替換映射集checkset中對應位置的元素為true;如果找到與參數cur(非标簽字元串)比對的元素,則替換為找到的元素;如果未找到,則預設替換為false。
第5211~5220行:在查找過程中,如果遇到已經檢查過的元素,則直接取該元素在候選集checkset中對應位置上的元素,避免重複查找。
第5222~5231行:如果參數cur是dom元素,則直接檢查找到的元素是否與之相等;如果參數cur是非标簽字元串,則調用方法sizzle.filter( expr, set, inplace, not )檢查是否與之比對。
第5237行:替換映射集checkset中對應位置的元素。變量match的初始值為false;如果找到與參數cur(dom元素)相等的元素,則其值變為true;如果找到與參數cur(非标簽字元串)比對的元素,則其值變為找到的元素。
3.8.6 dirnodecheck( dir,
cur, donename, checkset, nodecheck, isxml )
函數dirnodecheck( dir, cur, donename, checkset, nodecheck, isxml )負責周遊候選集checkset,檢查其中每個元素在某個方向dir上是否有與參數cur比對的元素。如果找到,則将候選集checkset中對應位置的元素替換為找到的元素;如果未找到,則替換為false。
checkset, part )中,當參數part是标簽時,才會調用函數dirnodecheck()。
5168 function
dirnodecheck( dir, cur, donename, checkset, nodecheck, isxml ) {
5169
5170
5171
5172
5173 var match = false;
5174
5175 elem = elem[dir];
5176
5177 while ( elem ) {
5178 if ( elem[ expando ] ===
5179 match =
checkset[elem.sizset];
5180 break;
5181 }
5182
5183 if ( elem.nodetype === 1
&& !isxml ){
5184 elem[ expando ] =
5185 elem.sizset = i;
5186 }
5187
5188 if (
elem.nodename.tolowercase() === cur ) {
5189 match = elem;
5190 break;
5191 }
5192
5193 elem = elem[dir];
5194
}
5195
5196 checkset[i] = match;
5197
5198
5199 }
第5168行:定義函數dirnodecheck( dir, cur, donename, checkset, nodecheck, isxml ),它接受6個參數:
參數cur:标簽字元串。
參數checkset:候選集,在查找過程中,其中的元素将被替換為與參數cur比對的元素或false。
參數nodecheck:标簽字元串。在後邊的代碼中沒有用到該參數。
第5169~5198行:周遊候選集checkset,對其中的每個元素,沿着某個方向(例如,“parentnode”、“previoussibling”)一直查找,直到找到節點名稱nodename與參數cur相等的元素,或者在該方向上不再有元素為止。如果找到,則替換映射集checkset中對應位置的元素為找到的元素;如果未找到,則預設替換為false。
第5178~5186行:在查找過程中,如果遇到已經檢查過的元素,則直接取該元素在候選集checkset中對應位置上的元素,避免重複查找。
第5196行:替換映射集checkset中對應位置的元素。變量match初始值為false,如果找到節點名稱nodename與參數cur相等的元素,則其值變為找到的元素。