jQuery Sizzle选择器(一)
1、瀏覽器對css選擇器采取逆向(從右向左)解析的原因:
如果正向解析,例如「div div p em」,我們首先就要檢查當前元素到 html 的整條路徑,找到最上層的div,再往下找,如果遇到不匹配就必須回到最上層那個 div,往下再去匹配選擇器中的第一個 div,回溯若干次才能確定匹配與否,效率很低。
逆向匹配則不同,如果當前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配時,才會不斷向上找父節點進行驗證。找到所有的em之后,再通過查找他的父元素是不是p來進行過濾。
2、Sizzle如果分解用戶傳入的css選擇器字符串
以”div > div.cl p span.red“為例
在Sizzle內部封裝了一個方法,該方法負責將css選擇器分解為一個數組。數組中的每一項是一個對象,格式如下:
{
"type" : "CLASS",
"value" : ".red",
"matchs" : " "
}
看一下tokenize的源碼:
// 假設傳入進來的選擇器是:div > p + .cl[type="checkbox"], #id:first-child
// 這里可以分為兩個規則:div > p + .aaron[type="checkbox"] 以及 #id:first-child
// 返回的需要是一個Token序列
// Sizzle的Token格式如下 :{value:'匹配到的字符串', type:'對應的Token類型', matches:'正則匹配到的一個結構'}
function tokenize( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ];
// 這里的soFar是表示目前還未分析的字符串剩余部分
// groups表示目前已經匹配到的規則組,在這個例子里邊,groups的長度最后是2,存放的是每個規則對應的Token序列
// 如果cache里邊有,直接拿出來即可
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
// 初始化
soFar = selector;
// 這是最后要返回的結果,一個二維數組
// 有多少個并聯選擇器,里面就有多少個數組,數組里面是擁有value與type的對象
groups = [];?
// 這里的預處理器為了對匹配到的Token適當做一些調整
// 自行查看源碼,其實就是正則匹配到的內容的一個預處理
preFilters = Expr.preFilter;
// 遞歸檢測字符串
// 比如"div > p + .cl input[type="checkbox"]"
while ( soFar ) {
// ?以第一個逗號切割選擇符,然后去掉前面的部分,處理同時傳入多個同級選擇器的情況,例如:$( "div, span" )
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// 如果匹配到逗號,將soFar中匹配到的部分刪除掉
soFar = soFar.slice( match[0].length ) || soFar;
}
// 往規則組里邊壓入一個Token序列,目前Token序列還是空的
groups.push( tokens = [] );
}
// 將matched重置為false,為下次判斷soFar中是否有內容做準備
matched = false;
// 將剛才前面的部分以關系選擇器再進行劃分
// 先處理這幾個特殊的Token : >, +, 空格, ~
// 因為他們比較簡單,并且是單字符的
if ( (match = rcombinators.exec( soFar )) ) {
// 獲取到匹配的字符
matched = match.shift();
// 放入Token序列中
tokens.push({
value: matched,
type: match[0].replace( rtrim, " " )
});
// 剩余還未分析的字符串需要減去這段已經分析過的
soFar = soFar.slice( matched.length );
}
// 這里開始分析這幾種Token : TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME
// 將每個選擇器組依次用ID,TAG,CLASS,ATTR,CHILD,PSEUDO這些正則進行匹配
// Expr.filter里邊對應地 就有這些key
//如果通過正則匹配到了Token格式:match = matchExpr[ type ].exec( soFar )
//然后看看需不需要預處理:!preFilters[ type ]
//如果需要 ,那么通過預處理器將匹配到的處理一下 : match = preFilters[ type ]( match )
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
(match = preFilters[ type ]( match ))) ) {
matched = match.shift();
//放入Token序列中
tokens.push({
value: matched,
type: type,
matches: match
});
//剩余還未分析的字符串需要減去這段已經分析過的
soFar = soFar.slice( matched.length );
}
}
//如果到了這里都還沒matched到,那么說明這個選擇器在這里有錯誤
//直接中斷詞法分析過程
//這就是Sizzle對詞法分析的異常處理
if ( !matched ) {
break;
}
}
//放到tokenCache函數里進行緩存
//如果只需要這個接口檢查選擇器的合法性,直接就返回soFar的剩余長度,倘若是大于零,說明選擇器不合法
//其余情況,如果soFar長度大于零,拋出異常;否則把groups記錄在cache里邊并返回,
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
tokenCache( selector, groups ).slice( 0 );
}
總結
以上是生活随笔為你收集整理的jQuery Sizzle选择器(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Install gevent in AI
- 下一篇: 关于浏览器对静态HTML页面的缓存问题