jQuery選擇器探究:ID選擇器
阿新 • • 發佈:2019-01-05
ID選擇器算是jQuery選擇器中最簡單的一種了,這裡跟蹤並梳理jQuery id選擇器的邏輯。
所有選擇器的呼叫語法都是從jQuery建構函式開始的,id選擇器的呼叫語法是:$("#x"),如果我們是jQuery的設計者,那麼在jQuery建構函式中對id選擇器的處理有兩種思路:要麼單獨處理,要麼與其他選擇器一樣處理,如果區別對待id選擇器,只需要識別選擇器/^#(\w)+$/即可,如果不區分於其他選擇器,那麼統一交給Sizzle處理,jQuery的設計者選擇了第一種方案。
由於jQuery建構函式jQuery.fn.init是及其靈活的,可以接受幾乎任意型別的引數組合,這裡只單獨分析selector引數為string型別時的邏輯:
可以看到selector引數為string時,先判斷是否為html標籤,$("<HTML TAG/?>(.*</HTML TAG>)?"),這種字串被jQuery單獨處理,我們將在其他篇中單獨分析。
接下來根據一個關鍵的正則表示式來判斷接受的selector是否為ID選擇器--quickExpr
quickExpr正則物件用於區分html tag和id selector,其有兩個捕獲分組,第一個分組捕獲html tag內容,第二個分組捕獲id識別符號,其依據正是特殊字元"#":#([\w\-]*)。match[1]存在時說明捕獲的是html tag內容,match[2]存在時說明捕獲的是id識別符號。這裡特別需要注意的是這行總的判斷程式碼:if ( match && (match[1] || !context) ),這裡並沒有使用if ( match && (match[1] || match[2]) ),說明在使用id selector時一定不能傳入第二個引數:$("#id", obj),否則將到傳入的第二個引數物件contenx中去查詢selector。這樣設計是有道理的:一般情況下#id直接在確定的document物件中查詢,不需要到特定的jQuery物件或原生dom物件中查詢。
判讀出selector是id selector後,重點是以下程式碼:
對於id選擇符,背後的邏輯還是回到基本api上了:"elem = document.getElementById( match[2] );"需要注意的點是,jQuery建構函式最終返回的物件肯定jQuery物件而不是原生的dom物件,因此查詢到的elem元素將作為返回的jQuery物件的第一個也是唯一一個元素,同時必須設定jQuery物件的context屬性和selector屬性,至於特殊的"捕獲id分組值與找到的原生物件的id屬性值不一致的"情況,是由於部分瀏覽器不遵守標準規範導致的(document.getElementById的實現為by name而不是by id),將轉入"return rootjQuery.find( selector );"邏輯,也就是除id selector外的通用Sizzle定位查詢邏輯中,我們將在接下來分析。
所有選擇器的呼叫語法都是從jQuery建構函式開始的,id選擇器的呼叫語法是:$("#x"),如果我們是jQuery的設計者,那麼在jQuery建構函式中對id選擇器的處理有兩種思路:要麼單獨處理,要麼與其他選擇器一樣處理,如果區別對待id選擇器,只需要識別選擇器/^#(\w)+$/即可,如果不區分於其他選擇器,那麼統一交給Sizzle處理,jQuery的設計者選擇了第一種方案。
由於jQuery建構函式jQuery.fn.init是及其靈活的,可以接受幾乎任意型別的引數組合,這裡只單獨分析selector引數為string型別時的邏輯:
// Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = quickExpr.exec( selector ); } // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; doc = (context ? context.ownerDocument || context : document); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; } return jQuery.merge( this, selector ); // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return (context || rootjQuery).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } }
可以看到selector引數為string時,先判斷是否為html標籤,$("<HTML TAG/?>(.*</HTML TAG>)?"),這種字串被jQuery單獨處理,我們將在其他篇中單獨分析。
接下來根據一個關鍵的正則表示式來判斷接受的selector是否為ID選擇器--quickExpr
// A simple way to check for HTML strings or ID strings // (both of which we optimize for) quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
quickExpr正則物件用於區分html tag和id selector,其有兩個捕獲分組,第一個分組捕獲html tag內容,第二個分組捕獲id識別符號,其依據正是特殊字元"#":#([\w\-]*)。match[1]存在時說明捕獲的是html tag內容,match[2]存在時說明捕獲的是id識別符號。這裡特別需要注意的是這行總的判斷程式碼:if ( match && (match[1] || !context) ),這裡並沒有使用if ( match && (match[1] || match[2]) ),說明在使用id selector時一定不能傳入第二個引數:$("#id", obj),否則將到傳入的第二個引數物件contenx中去查詢selector。這樣設計是有道理的:一般情況下#id直接在確定的document物件中查詢,不需要到特定的jQuery物件或原生dom物件中查詢。
判讀出selector是id selector後,重點是以下程式碼:
// HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; }
對於id選擇符,背後的邏輯還是回到基本api上了:"elem = document.getElementById( match[2] );"需要注意的點是,jQuery建構函式最終返回的物件肯定jQuery物件而不是原生的dom物件,因此查詢到的elem元素將作為返回的jQuery物件的第一個也是唯一一個元素,同時必須設定jQuery物件的context屬性和selector屬性,至於特殊的"捕獲id分組值與找到的原生物件的id屬性值不一致的"情況,是由於部分瀏覽器不遵守標準規範導致的(document.getElementById的實現為by name而不是by id),將轉入"return rootjQuery.find( selector );"邏輯,也就是除id selector外的通用Sizzle定位查詢邏輯中,我們將在接下來分析。