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下面div.getAttribute("className")会得到class support.attributes = assert(function( div ) { div.className = "i"; return !div.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements //检查getElementsByTagName是否仅仅返回Element元素 support.getElementsByTagName = assert(function( div ) { div.appendChild( doc.createComment("") ); return !div.getElementsByTagName("*").length; }); // Check if getElementsByClassName can be trusted //检查getElementsByClassName能否正确处理多class的情况 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { div.innerHTML = "<div class='a'></div><div class='a i'></div>"; // Support: Safari<4 // Catch class over-caching div.firstChild.className = "i"; // Support: Opera<10 // Catch gEBCN failure to find non-leading classes return div.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( div ) { docElem.appendChild( div ).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 http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { // 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 // http://bugs.jquery.com/ticket/12359 div.innerHTML = "<select t=''><option selected=''></option></select>"; // Support: IE8, Opera 10-12 // Nothing should be selected when empty strings follow ^= or $= or *= if ( div.querySelectorAll("[t^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !div.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } }); assert(function( div ) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( div.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 ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( div ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( div, "div" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( div, "[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; };