JQuery日記 5.11 Sizzle選擇器(五)

//設置當前document和document對應的變量和方法
setDocument = Sizzle.setDocument = function( node ) {
	var hasCompare,
		//node為Element時返回node所屬document
		//node為Document時返回node
		//node為空時返回window.document
		doc = node ? node.ownerDocument || node : preferredDoc,
		//document所屬window
		parent = doc.defaultView;

	// If no document and documentElement is available, return
	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
		return document;
	}

	// Set our document
	//設置全局的document為當前doc
	document = doc;
	docElem = doc.documentElement;

	// Support tests
	documentIsHTML = !isXML( doc );

	// Support: IE>8
	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
	// IE6-8 do not support the defaultView property so parent will be undefined
	
	/* 
	 * 如果 iframe文檔已經指定瞭document並且如果iframe重載
	 * 當訪問document變量時,IE將拋出"permission denied"錯誤
	 * IE6-8不支持defaultView所以parent變量為undefined
	 */
	
	//所以要在unload的時候重新setDocument()
	//這個判斷說明當前代碼在frame中 
	if ( parent && parent !== parent.top ) {
		// IE11 does not have attachEvent, so all must suffer
		//frame卸載時重新設置document
		if ( parent.addEventListener ) {
			parent.addEventListener( "unload", function() {
				//未傳入參數document=window.document
				setDocument();
			}, false );
		} else if ( parent.attachEvent ) {
			parent.attachEvent( "onunload", function() {
				setDocument();
			});
		}
	}
	
	//一些能力檢查,添加到support中
	/* Attributes
	---------------------------------------------------------------------- */

	// Support: IE<8
	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
	// IE8以下p.getAttribute("className")會得到class
	support.attributes = assert(function( p ) {
		p.className = "i";
		return !p.getAttribute("className");
	});

	/* getElement(s)By*
	---------------------------------------------------------------------- */

	// Check if getElementsByTagName("*") returns only elements
	//檢查getElementsByTagName是否隻返回Element元素
	support.getElementsByTagName = assert(function( p ) {
		p.appendChild( doc.createComment("") );
		return !p.getElementsByTagName("*").length;
	});

	// Check if getElementsByClassName can be trusted
	//檢查getElementsByClassName是否能正確處理多class的情況
	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( p ) {
		p.innerHTML = "

"; // Support: Safari<4 // Catch class over-caching p.firstChild.className = "i"; // Support: Opera<10 // Catch gEBCN failure to find non-leading classes return p.getElementsByClassName("i").length === 2; }); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programatically-set names, // so use a roundabout getElementsByName test //檢查是否getElementById會根據name返回元素 support.getById = assert(function( p ) { docElem.appendChild( p ).id = expando; return !doc.getElementsByName || !doc.getElementsByName( expando ).length; }); // ID find and filter if ( support.getById ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== strundefined && documentIsHTML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }; Expr.filter["ID"] = function( id ) { //將轉義字符轉回字符串 var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; } else { // Support: IE6/7 // getElementById is not reliable as a find shortcut //IE6,7的getElementById因為會根據name返回元素 //所以是不能用原生getElementById方法獲取元素的 //所以刪除 delete Expr.find["ID"]; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } // Tag //getElementsByTagName是否隻返回Element元素 Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== strundefined ) { return context.getElementsByTagName( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( p ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // https://bugs.jquery.com/ticket/12359 p.innerHTML = ""; // Support: IE8, Opera 10-12 // Nothing should be selected when empty strings follow ^= or $= or *= if ( p.querySelectorAll("[t^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !p.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Webkit/Opera - :checked should return selected option elements // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !p.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } }); assert(function( p ) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); p.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( p.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( !p.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos p.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( p ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( p, "p" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( p, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself // 當節點contains自己時,返回true contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { //a是Document,adown為HtmlElement其他為a var adown = a.nodeType === 9 ? a.documentElement : a, //bup是b節點的父節點 bup = b && b.parentNode; //(1)如果a是b的父節點,快速返回true //(2)如果b有父節點並且父節點是Element // a包含b的父節點返回true return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? //contains的是bup而是b是為瞭避免contains自己時返回true adown.contains( bup ) : //為什麼要&16,請看compareDocumentPosition的返回值 /* 000000 0 元素一致 000001 1 節點在不同的文檔(或者一個在文檔之外) 000010 2 節點 B 在節點 A 之前 000100 4 節點 A 在節點 B 之前 001000 8 節點 B 包含節點 A 010000 16 節點 A 包含節點 B 100000 32 瀏覽器的私有使用 */ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : //沒有原生方法時迭代判斷 function( a, b ) { if ( b ) { //b的某個祖先節點===a說明a包含b while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition // 如果隻有一個元素擁有排序方法. // a節點有排序方法返回-1,b節點有排序方法返回1 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } // Calculate position if both inputs belong to the same document // 如果兩個元素屬於一個文檔計算位置 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : // Otherwise we know they are disconnected 1; // Disconnected nodes // 如果節點不再同一文檔 或者 不支持分離排序並且a,b不再同一文檔 if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document // a節點包含在當前document返回-1 if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } // b節點包含在當前doucment返回1 if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } // Maintain original order // 保持原有順序 return sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } //compare //a在b之前返回-1,否則返回1 return compare & 4 ? -1 : 1; } : function( a, b ) { // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected //沒有父親節點說明可能是document或者以不再DOM樹中的節點 if ( !aup || !bup ) { //如果a是document說明a在前返回-1 //如果b是document說明b在前返回1 //否則如果a有父節點說明b是disconnected返回-1 //否則b有父節點a是disconnected返回1 //如果a、b都是disconnected,使用indexOf方法 return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check // 父親是一個,使用siblingCheck(a,b)方法快速檢查 } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison // 否則需要遍歷整個祖先 cur = a; while ( (cur = cur.parentNode) ) { //將a的所有的祖先入棧 ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { //將b的所有的祖先入棧 bp.unshift( cur ); } // Walk down the tree looking for a discrepancy // 如果同層的祖先是一個 while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor // 比較a,b的同級一個祖先之下的兩個兄弟節點的位置 siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; //返回document return doc; };

發佈留言