jQuery源码学习笔记七
在Sizzle中有许多有用的辅助方法,我们继续一个个看。其中涉及许多BUG的修正以及一些很少见的API。
//@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved var sortOrder;//比较两个元素在页面上的顺序,返回正数,0,负数 //如果支持compareDocumentPosition方法,新锐的标准浏览器都支持 //我在《javascript contains方法》一文中有详细介绍 //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { //节点a 在节点b 之前, var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; //用于IE //sourceIndex是指元素在NodeList中的位置 } else if ( "sourceIndex" in document.documentElement ) { sortOrder = function( a, b ) { var ret = a.sourceIndex - b.sourceIndex; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; //用于旧式的标准游览器 } else if ( document.createRange ) { sortOrder = function( a, b ) { var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.selectNode(a); aRange.collapse(true); bRange.selectNode(b); bRange.collapse(true); //比较两个selection的位置 //https://developer.mozilla.org/en/DOM/range.compareBoundaryPoints var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); if ( ret === 0 ) { hasDuplicate = true; } return ret; }; }
比较元素位置在IE还可以用uniqueNumber,都是自上至下分配数字。
下面对getElementById,getElementsByTagName,getElementsByClassName, querySelectorAll 进行调整。
//在getElementById(XXX)在IE中有bug,它会找第一个属性name或id等于XXX的元素, //尤其是在表单元素中,它们通常都带有name属性 (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("form"), id = "script" + (new Date).getTime(); form.innerHTML = "<input name='" + id + "'/>"; // Inject it into the root element, check its status, and remove it quickly var root = document.documentElement; root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( !!document.getElementById( id ) ) { //重载一下Expr.find.ID Expr.find.ID = function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); //确定此元素是否显式为id赋值 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function(elem, match){ //确定此元素是否显式为id赋值 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { //重载Expr.find.TAG Expr.find.TAG = function(match, context){ var results = context.getElementsByTagName(match[1]); // Filter out possible comments //返回其所有元素节点后代,组成纯数组 if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes //处理href属性,如果第二个参数,IE返回的是绝对路径 div.innerHTML = "<a href='#'></a>"; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function(elem){ return elem.getAttribute("href", 2); }; } })(); if ( document.querySelectorAll ) (function(){ //创建一个元素片段<div><p class='TEST'></p></div> //用querySelectorAll看看能否正确找到这个p元素 var oldSizzle = Sizzle, div = document.createElement("div"); div.innerHTML = "<p class='TEST'></p>"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } //如果能,就用querySelectorAll重载整个Sizzle引擎,效率最高!!! Sizzle = function(query, context, extra, seed){ context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && context.nodeType === 9 && !isXML(context) ) { try { return makeArray( context.querySelectorAll(query), extra ); } catch(e){} } return oldSizzle(query, context, extra, seed); }; Sizzle.find = oldSizzle.find; Sizzle.filter = oldSizzle.filter; Sizzle.selectors = oldSizzle.selectors; Sizzle.matches = oldSizzle.matches; })(); if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ //创建一个元素片段<div><div class='test e'></div><div class='test'></div></div> //用getElementsByClassName看看能否正确找到这两个div元素 var div = document.createElement("div"); div.innerHTML = "<div class='test e'></div><div class='test'></div>"; // Opera can't find a second classname (in 9.6) if ( div.getElementsByClassName("e").length === 0 ) return; // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) return; //重新调整与CLASS有关的逻辑 Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function(match, context, isXML) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; })();
//这东西用于后代选择器与兄长选择器,取得某范围中所有元素,并且防止重复取得 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; //checkSet为元素集合,doneName为数字 for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ){ elem.sizcache = doneName;//设置一标记,以后有与它值相等的不重复取 elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) {//比较是否相等 match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem.sizcache = doneName; elem.sizset = i; } if ( elem.nodeName === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } //和上面功能差不多,不知是否出于兼容以前版本的需要…… function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ) { elem.sizcache = doneName; elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem.sizcache = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } }
//判断一个元素是否包含另一个元素 //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html var contains = document.compareDocumentPosition ? function(a, b){ return a.compareDocumentPosition(b) & 16; } : function(a, b){ return a !== b && (a.contains ? a.contains(b) : true); }; //判断是否为XML var isXML = function(elem){ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || !!elem.ownerDocument && isXML( elem.ownerDocument ); }; //主要是处理结构伪类中的子元素过滤器 var posProcess = function(selector, context){ var tmpSet = [], later = "", match, root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } //如果不是在亲子中选择,就是在它的所有后代中选择“*” selector = Expr.relative[selector] ? selector + "*" : selector; //回调Sizzle for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet ); } return Sizzle.filter( later, tmpSet ); };
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年