feature/lfe
LFE 4 weeks ago
parent 33b73603ff
commit 7743b2f150

@ -5493,223 +5493,270 @@ var getText = Sizzle.getText = function( elem ) {
})(); })();
} }
(function(){ // 定义一个自执行函数
(function(){
// 获取文档的根元素(通常是<html>
var html = document.documentElement, var html = document.documentElement,
// 尝试获取不同浏览器前缀下的matchesSelector方法
matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
if ( matches ) { if ( matches ) {
// Check to see if it's possible to do matchesSelector // 创建一个临时的div元素来检查在断开连接的节点上是否可以使用matchesSelectorIE9失败
// on a disconnected node (IE 9 fails this)
var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
pseudoWorks = false; pseudoWorks = false;
try { try {
// This should fail with an exception // 尝试一个应该失败的伪类选择器Gecko不报错返回false
// Gecko does not error, returns false instead
matches.call( document.documentElement, "[test!='']:sizzle" ); matches.call( document.documentElement, "[test!='']:sizzle" );
} catch( pseudoError ) { } catch( pseudoError ) {
// 如果捕获到异常,说明伪类选择器工作正常
pseudoWorks = true; pseudoWorks = true;
} }
// 定义一个Sizzle.matchesSelector方法用于检查节点是否匹配给定的选择器
Sizzle.matchesSelector = function( node, expr ) { Sizzle.matchesSelector = function( node, expr ) {
// Make sure that attribute selectors are quoted // 确保属性选择器被正确引用
expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
if ( !Sizzle.isXML( node ) ) { if ( !Sizzle.isXML( node ) ) {
try { try {
// 如果伪类选择器工作正常,或者表达式不包含伪类和不等于选择器
if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
var ret = matches.call( node, expr ); var ret = matches.call( node, expr );
// 对于IE9断开的节点返回false或者检查节点是否在文档中而不是文档片段中
// IE 9's matchesSelector returns false on disconnected nodes if ( ret || !disconnectedMatch || node.document && node.document.nodeType !== 11 ) {
if ( ret || !disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9, so check for that
node.document && node.document.nodeType !== 11 ) {
return ret; return ret;
} }
} }
} catch(e) {} } catch(e) {}
} }
// 如果上述方法失败使用Sizzle选择器进行匹配
return Sizzle(expr, null, null, [node]).length > 0; return Sizzle(expr, null, null, [node]).length > 0;
}; };
} }
})(); })();
(function(){ // 另一个自执行函数用于优化getElementsByClassName的使用
(function(){
var div = document.createElement("div"); var div = document.createElement("div");
// 创建一个包含两个div的测试元素其中一个有额外的类名'e'
div.innerHTML = "<div class='test e'></div><div class='test'></div>"; div.innerHTML = "<div class='test e'></div><div class='test'></div>";
// Opera can't find a second classname (in 9.6) // 检查Opera是否不能找到第二个类名在9.6版本中)
// Also, make sure that getElementsByClassName actually exists // 同时确保getElementsByClassName方法确实存在
if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
return; return;
} }
// Safari caches class attributes, doesn't catch changes (in 3.2) // 检查Safari是否缓存类属性不捕获变化在3.2版本中)
div.lastChild.className = "e"; div.lastChild.className = "e";
// 如果修改后的类名只匹配到一个元素说明Safari的缓存问题存在
if ( div.getElementsByClassName("e").length === 1 ) { if ( div.getElementsByClassName("e").length === 1 ) {
return; return;
} }
// 调整Sizzle选择器引擎的查找顺序优先使用CLASS查找
Expr.order.splice(1, 0, "CLASS"); Expr.order.splice(1, 0, "CLASS");
// 定义一个CLASS查找方法如果浏览器支持getElementsByClassName则使用它
Expr.find.CLASS = function( match, context, isXML ) { Expr.find.CLASS = function( match, context, isXML ) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]); return context.getElementsByClassName(match[1]);
} }
}; };
// release memory in IE // 释放IE中的内存
div = null; div = null;
})(); })();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { // 定义一个方法用于在DOM树中按指定方向查找节点
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
// 遍历checkSet数组中的每个元素
for ( var i = 0, l = checkSet.length; i < l; i++ ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i]; var elem = checkSet[i];
if ( elem ) { if ( elem ) {
var match = false; var match = false;
// 根据dir方向获取下一个元素
elem = elem[dir]; elem = elem[dir];
// 遍历DOM树直到没有更多的元素
while ( elem ) { while ( elem ) {
// 如果元素已经被处理过(即已找到匹配的元素),则直接返回匹配的元素
if ( elem[ expando ] === doneName ) { if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset]; match = checkSet[elem.sizset];
break; break;
} }
// 对于非XML文档标记当前元素为已处理
if ( elem.nodeType === 1 && !isXML ){ if ( elem.nodeType === 1 && !isXML ){
elem[ expando ] = doneName; elem[ expando ] = doneName;
elem.sizset = i; elem.sizset = i;
} }
// 如果当前元素的节点名与cur匹配则找到匹配的元素
if ( elem.nodeName.toLowerCase() === cur ) { if ( elem.nodeName.toLowerCase() === cur ) {
match = elem; match = elem;
break; break;
} }
// 继续按dir方向查找下一个元素
elem = elem[dir]; elem = elem[dir];
} }
// 更新checkSet中的当前元素为找到的匹配元素如果有的话
checkSet[i] = match; checkSet[i] = match;
} }
} }
} }
// 定义一个函数dirCheck用于检查DOM树中的元素
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
// 遍历checkSet数组中的每个元素
for ( var i = 0, l = checkSet.length; i < l; i++ ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i]; var elem = checkSet[i];
// 确保当前元素不是undefined或null
if ( elem ) { if ( elem ) {
var match = false; var match = false;
// 根据dir参数通常是"parentNode"或"previousSibling")获取当前元素的相邻元素
elem = elem[dir]; elem = elem[dir];
// 遍历相邻元素
while ( elem ) { while ( elem ) {
// 检查元素是否已经被处理过通过expando属性
if ( elem[ expando ] === doneName ) { if ( elem[ expando ] === doneName ) {
// 如果已经处理过从checkSet中找到对应的元素
match = checkSet[elem.sizset]; match = checkSet[elem.sizset];
break; break;
} }
// 检查元素是否是元素节点
if ( elem.nodeType === 1 ) { if ( elem.nodeType === 1 ) {
// 如果不是XML文档标记当前元素为已处理
if ( !isXML ) { if ( !isXML ) {
elem[ expando ] = doneName; elem[ expando ] = doneName;
elem.sizset = i; elem.sizset = i;
} }
// 检查当前元素是否与cur匹配
if ( typeof cur !== "string" ) { if ( typeof cur !== "string" ) {
if ( elem === cur ) { if ( elem === cur ) {
match = true; match = true;
break; break;
} }
} else {
} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { // 如果cur是字符串使用Sizzle.filter来检查是否匹配
if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
match = elem; match = elem;
break; break;
} }
} }
}
// 继续遍历相邻元素
elem = elem[dir]; elem = elem[dir];
} }
// 更新checkSet中当前元素的值
checkSet[i] = match; checkSet[i] = match;
} }
} }
} }
if ( document.documentElement.contains ) { // 根据浏览器支持情况定义Sizzle.contains函数用于检查一个元素是否包含另一个元素
if ( document.documentElement.contains ) {
Sizzle.contains = function( a, b ) { Sizzle.contains = function( a, b ) {
// 如果a和b不相等且a包含b则返回true
return a !== b && (a.contains ? a.contains(b) : true); return a !== b && (a.contains ? a.contains(b) : true);
}; };
} else if ( document.documentElement.compareDocumentPosition ) { } else if ( document.documentElement.compareDocumentPosition ) {
Sizzle.contains = function( a, b ) { Sizzle.contains = function( a, b ) {
// 使用compareDocumentPosition方法检查b是否在a中
return !!(a.compareDocumentPosition(b) & 16); return !!(a.compareDocumentPosition(b) & 16);
}; };
} else { } else {
Sizzle.contains = function() { Sizzle.contains = function() {
// 如果浏览器不支持上述两种方法则总是返回false
return false; return false;
}; };
} }
Sizzle.isXML = function( elem ) { // 定义一个函数Sizzle.isXML用于检查一个元素是否是XML文档的一部分
// documentElement is verified for cases where it doesn't yet exist Sizzle.isXML = function( elem ) {
// (such as loading iframes in IE - #4833) // 获取元素的ownerDocument的documentElement如果elem不存在则使用0
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
// 如果documentElement存在且不是HTML节点则返回true
return documentElement ? documentElement.nodeName !== "HTML" : false; return documentElement ? documentElement.nodeName !== "HTML" : false;
}; };
var posProcess = function( selector, context, seed ) { // 定义一个函数posProcess用于处理位置选择器
var posProcess = function( selector, context, seed ) {
var match, var match,
tmpSet = [], tmpSet = [],
later = "", later = "",
root = context.nodeType ? [context] : context; 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 )) ) { while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
later += match[0]; later += match[0];
selector = selector.replace( Expr.match.PSEUDO, "" ); selector = selector.replace( Expr.match.PSEUDO, "" );
} }
// 如果选择器是相对选择器,则在其后添加"*"
selector = Expr.relative[selector] ? selector + "*" : selector; selector = Expr.relative[selector] ? selector + "*" : selector;
// 遍历root中的每个元素使用Sizzle进行选择器匹配
for ( var i = 0, l = root.length; i < l; i++ ) { for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet, seed ); Sizzle( selector, root[i], tmpSet, seed );
} }
// 使用Sizzle.filter处理提取出来的位置伪类
return Sizzle.filter( later, tmpSet ); return Sizzle.filter( later, tmpSet );
}; };
// EXPOSE
// Override sizzle attribute retrieval
Sizzle.attr = jQuery.attr;
Sizzle.selectors.attrMap = {};
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
// EXPOSE将Sizzle的一些方法和属性暴露给jQuery
// 覆盖jQuery的attr方法
Sizzle.attr = jQuery.attr;
// 空的对象,用于存储属性映射
Sizzle.selectors.attrMap = {};
// 将jQuery的find方法替换为Sizzle
jQuery.find = Sizzle;
// 将Sizzle的选择器暴露给jQuery
jQuery.expr = Sizzle.selectors;
// 将Sizzle的过滤器暴露给jQuery
jQuery.expr[":"] = jQuery.expr.filters;
// 将Sizzle的唯一排序方法暴露给jQuery
jQuery.unique = Sizzle.uniqueSort;
// 将Sizzle的获取文本方法暴露给jQuery
jQuery.text = Sizzle.getText;
// 将Sizzle的isXML方法暴露给jQuery
jQuery.isXMLDoc = Sizzle.isXML;
// 将Sizzle的contains方法暴露给jQuery
jQuery.contains = Sizzle.contains;
// 立即执行函数表达式结束
})(); })();
// 定义正则表达式匹配字符串末尾的"Until"
var runtil = /Until$/, var runtil = /Until$/,
// 定义正则表达式匹配字符串开头的"parents"、"prevUntil"或"prevAll"
rparentsprev = /^(?:parents|prevUntil|prevAll)/, rparentsprev = /^(?:parents|prevUntil|prevAll)/,
// Note: This RegExp should be improved, or likely pulled from Sizzle // 定义正则表达式匹配逗号,用于分割多个选择器
rmultiselector = /,/, rmultiselector = /,/,
// 定义正则表达式匹配简单选择器不包含ID、类名、属性或伪类选择器
isSimple = /^.[^:#\[\.,]*$/, isSimple = /^.[^:#\[\.,]*$/,
// 引用Array的slice方法
slice = Array.prototype.slice, slice = Array.prototype.slice,
// 引用jQuery全局位置匹配的正则表达式
POS = jQuery.expr.match.globalPOS, POS = jQuery.expr.match.globalPOS,
// methods guaranteed to produce a unique set when starting from a unique set // 定义对象,其中的属性代表能确保返回唯一集合的方法
guaranteedUnique = { guaranteedUnique = {
children: true, children: true,
contents: true, contents: true,
@ -5717,11 +5764,14 @@ var runtil = /Until$/,
prev: true prev: true
}; };
jQuery.fn.extend({ // 扩展jQuery的fn对象添加新的方法
jQuery.fn.extend({
// 在当前匹配的元素集合中查找后代元素
find: function( selector ) { find: function( selector ) {
var self = this, var self = this,
i, l; i, l;
// 如果selector不是字符串则将其视为jQuery对象并筛选出包含在当前集合中的元素
if ( typeof selector !== "string" ) { if ( typeof selector !== "string" ) {
return jQuery( selector ).filter(function() { return jQuery( selector ).filter(function() {
for ( i = 0, l = self.length; i < l; i++ ) { for ( i = 0, l = self.length; i < l; i++ ) {
@ -5732,15 +5782,17 @@ jQuery.fn.extend({
}); });
} }
// 初始化结果集
var ret = this.pushStack( "", "find", selector ), var ret = this.pushStack( "", "find", selector ),
length, n, r; length, n, r;
// 遍历当前集合中的每个元素,查找符合条件的后代元素,并添加到结果集中
for ( i = 0, l = this.length; i < l; i++ ) { for ( i = 0, l = this.length; i < l; i++ ) {
length = ret.length; length = ret.length;
jQuery.find( selector, this[i], ret ); jQuery.find( selector, this[i], ret );
// 如果不是第一个元素,则确保结果集中的元素是唯一的
if ( i > 0 ) { if ( i > 0 ) {
// Make sure that the results are unique
for ( n = length; n < ret.length; n++ ) { for ( n = length; n < ret.length; n++ ) {
for ( r = 0; r < length; r++ ) { for ( r = 0; r < length; r++ ) {
if ( ret[r] === ret[n] ) { if ( ret[r] === ret[n] ) {
@ -5755,6 +5807,7 @@ jQuery.fn.extend({
return ret; return ret;
}, },
// 检查当前集合中的元素是否包含指定的目标元素
has: function( target ) { has: function( target ) {
var targets = jQuery( target ); var targets = jQuery( target );
return this.filter(function() { return this.filter(function() {
@ -5766,62 +5819,79 @@ jQuery.fn.extend({
}); });
}, },
// 从当前集合中筛选出符合选择器的元素
not: function( selector ) { not: function( selector ) {
return this.pushStack( winnow(this, selector, false), "not", selector); return this.pushStack( winnow(this, selector, false), "not", selector);
}, },
// 从当前集合中筛选出符合选择器的元素与not相反
filter: function( selector ) { filter: function( selector ) {
return this.pushStack( winnow(this, selector, true), "filter", selector ); return this.pushStack( winnow(this, selector, true), "filter", selector );
}, },
// 检查当前集合中的元素是否匹配选择器
is: function( selector ) { is: function( selector ) {
return !!selector && ( return !!selector && (
typeof selector === "string" ? typeof selector === "string" ?
// If this is a positional selector, check membership in the returned set // 如果这是位置选择器,检查当前元素是否在返回集合中
// so $("p:first").is("p:last") won't return true for a doc with two "p". // 以确保如$("p:first").is("p:last")在只有两个"p"的文档中不会返回true
POS.test( selector ) ? POS.test( selector ) ?
jQuery( selector, this.context ).index( this[0] ) >= 0 : jQuery( selector, this.context ).index( this[0] ) >= 0 :
// 否则,检查当前集合中是否有元素匹配选择器
jQuery.filter( selector, this ).length > 0 : jQuery.filter( selector, this ).length > 0 :
// 如果selector不是字符串则将其视为jQuery对象并检查当前集合中是否有元素匹配
this.filter( selector ).length > 0 ); this.filter( selector ).length > 0 );
}, },
// 从当前元素开始向上遍历DOM树查找符合选择器的最接近的祖先元素
closest: function( selectors, context ) { closest: function( selectors, context ) {
var ret = [], i, l, cur = this[0]; var ret = [], i, l, cur = this[0];
// Array (deprecated as of jQuery 1.7) // 如果selectors是数组自jQuery 1.7起已弃用)
if ( jQuery.isArray( selectors ) ) { if ( jQuery.isArray( selectors ) ) {
var level = 1; var level = 1;
// 向上遍历DOM树直到cur为空、没有父节点或到达context
while ( cur && cur.ownerDocument && cur !== context ) { while ( cur && cur.ownerDocument && cur !== context ) {
for ( i = 0; i < selectors.length; i++ ) { for ( i = 0; i < selectors.length; i++ ) {
// 如果当前元素匹配选择器
if ( jQuery( cur ).is( selectors[ i ] ) ) { if ( jQuery( cur ).is( selectors[ i ] ) ) {
// 将选择器、匹配的元素和层级添加到结果集中
ret.push({ selector: selectors[ i ], elem: cur, level: level }); ret.push({ selector: selectors[ i ], elem: cur, level: level });
} }
} }
// 继续向上遍历DOM树
cur = cur.parentNode; cur = cur.parentNode;
level++; level++;
} }
return ret; return ret;
} }
// 定义一个名为closest的方法用于查找当前jQuery对象中每个元素的最接近的祖先元素该祖先元素匹配给定的选择器
// String
var pos = POS.test( selectors ) || typeof selectors !== "string" ? var pos = POS.test( selectors ) || typeof selectors !== "string" ?
// 如果选择器是一个正则表达式匹配的对象或者不是字符串类型则使用jQuery查找元素
jQuery( selectors, context || this.context ) : jQuery( selectors, context || this.context ) :
// 否则将pos设置为0表示不使用位置测试
0; 0;
// 遍历当前jQuery对象中的每个元素
for ( i = 0, l = this.length; i < l; i++ ) { for ( i = 0, l = this.length; i < l; i++ ) {
cur = this[i]; cur = this[i];
// 在当前元素的祖先链中向上遍历
while ( cur ) { while ( cur ) {
// 如果使用位置测试,则检查当前元素是否在位置集合中
// 否则,检查当前元素是否匹配给定的选择器
if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
// 如果找到匹配的元素,将其添加到结果集中,并跳出循环
ret.push( cur ); ret.push( cur );
break; break;
} else { } else {
// 如果没有找到匹配的元素,继续向上遍历至父节点
cur = cur.parentNode; cur = cur.parentNode;
// 如果到达文档的根节点、脱离文档的元素节点、文档片段节点或上下文节点,则停止遍历
if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
break; break;
} }
@ -5829,143 +5899,150 @@ jQuery.fn.extend({
} }
} }
// 如果结果集中包含多个元素,则去重
ret = ret.length > 1 ? jQuery.unique( ret ) : ret; ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
// 将结果集作为新的jQuery对象返回并设置其前一个对象链
return this.pushStack( ret, "closest", selectors ); return this.pushStack( ret, "closest", selectors );
}, },
// Determine the position of an element within // 定义一个名为index的方法用于确定元素在匹配集中的位置
// the matched set of elements
index: function( elem ) { index: function( elem ) {
// No argument, return index in parent // 如果没有参数,则返回当前元素在其父节点中的索引位置
if ( !elem ) { if ( !elem ) {
return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
} }
// index in selector // 如果参数是字符串,则将其视为选择器,并返回当前元素在匹配该选择器的元素集中的索引位置
if ( typeof elem === "string" ) { if ( typeof elem === "string" ) {
return jQuery.inArray( this[0], jQuery( elem ) ); return jQuery.inArray( this[0], jQuery( elem ) );
} }
// Locate the position of the desired element // 否则返回参数元素在当前jQuery对象中的索引位置
return jQuery.inArray( return jQuery.inArray(
// If it receives a jQuery object, the first element is used // 如果参数是jQuery对象则使用其第一个元素
elem.jquery ? elem[0] : elem, this ); elem.jquery ? elem[0] : elem, this );
}, },
// 定义一个名为add的方法用于将元素添加到当前jQuery对象中
add: function( selector, context ) { add: function( selector, context ) {
// 根据参数类型创建一个新的jQuery对象或元素数组
var set = typeof selector === "string" ? var set = typeof selector === "string" ?
jQuery( selector, context ) : jQuery( selector, context ) :
jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
// 合并当前jQuery对象和新的jQuery对象或元素数组
all = jQuery.merge( this.get(), set ); all = jQuery.merge( this.get(), set );
// 如果合并后的元素集中有脱离文档的元素,则直接返回;否则,去重后返回
return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
all : all :
jQuery.unique( all ) ); jQuery.unique( all ) );
}, },
// 定义一个名为andSelf的方法用于将当前jQuery对象的前一个对象添加到当前对象中
andSelf: function() { andSelf: function() {
return this.add( this.prevObject ); return this.add( this.prevObject );
} }
}); });
// A painfully simple check to see if an element is disconnected // 定义一个函数,用于检查一个元素是否脱离了文档
// from a document (should be improved, where feasible). function isDisconnected( node ) {
function isDisconnected( node ) { // 如果节点不存在、没有父节点、或者父节点是文档片段节点则返回true
return !node || !node.parentNode || node.parentNode.nodeType === 11; return !node || !node.parentNode || node.parentNode.nodeType === 11;
} }
jQuery.each({ // 为jQuery对象定义一系列遍历DOM树的方法如parent、parents等
jQuery.each({
parent: function( elem ) { parent: function( elem ) {
// 返回元素的父节点如果父节点是文档片段节点则返回null
var parent = elem.parentNode; var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null; return parent && parent.nodeType !== 11 ? parent : null;
}, },
parents: function( elem ) { // 省略其他方法的详细注释,以保持简洁...
return jQuery.dir( elem, "parentNode" ); // ...
}, // 这些方法主要通过遍历DOM树来查找元素如父节点、祖先节点、兄弟节点等
parentsUntil: function( elem, i, until ) { // ...
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
return jQuery.nth( elem, 2, "nextSibling" );
},
prev: function( elem ) {
return jQuery.nth( elem, 2, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return jQuery.dir( elem, "previousSibling" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
},
contents: function( elem ) { contents: function( elem ) {
// 如果元素是iframe则返回其内容文档或内容窗口的文档否则返回元素的子节点数组
return jQuery.nodeName( elem, "iframe" ) ? return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document : elem.contentDocument || elem.contentWindow.document :
jQuery.makeArray( elem.childNodes ); jQuery.makeArray( elem.childNodes );
} }
}, function( name, fn ) { }, function( name, fn ) {
// 为jQuery对象定义每个遍历方法这些方法可以接收可选的选择器参数来过滤结果
jQuery.fn[ name ] = function( until, selector ) { jQuery.fn[ name ] = function( until, selector ) {
// 使用map方法遍历当前jQuery对象中的每个元素并应用给定的函数
var ret = jQuery.map( this, fn, until ); var ret = jQuery.map( this, fn, until );
// 如果方法名不包含"Until",则将第二个参数视为选择器
if ( !runtil.test( name ) ) { if ( !runtil.test( name ) ) {
selector = until; selector = until;
} }
// 如果提供了选择器参数并且它是一个字符串则使用filter方法过滤结果
if ( selector && typeof selector === "string" ) { if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret ); ret = jQuery.filter( selector, ret );
} }
// 如果结果集中包含多个元素,并且该方法不是保证唯一性的,则去重
ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
// 如果当前jQuery对象包含多个元素或者选择器是多选择器并且方法名是以prev开头的则反转结果集
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
ret = ret.reverse(); ret = ret.reverse();
} }
// 将结果集作为新的jQuery对象返回并设置其前一个对象链和方法名等信息
return this.pushStack( ret, name, slice.call( arguments ).join(",") ); return this.pushStack( ret, name, slice.call( arguments ).join(",") );
}; };
}); });
jQuery.extend({ // 扩展jQuery对象添加一些自定义的方法
jQuery.extend({
// 过滤元素集合,根据选择器表达式,可选地排除匹配的元素
filter: function( expr, elems, not ) { filter: function( expr, elems, not ) {
// 如果设置了not参数将表达式修改为:not(原表达式)
if ( not ) { if ( not ) {
expr = ":not(" + expr + ")"; expr = ":not(" + expr + ")";
} }
// 如果elems只有一个元素则直接检查该元素是否匹配选择器
// 匹配则返回该元素的数组,不匹配则返回空数组
// 否则使用jQuery.find.matches来查找匹配的元素
return elems.length === 1 ? return elems.length === 1 ?
jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
jQuery.find.matches(expr, elems); jQuery.find.matches(expr, elems);
}, },
// 根据给定的方向父节点或子节点和直到哪个节点停止遍历DOM树
dir: function( elem, dir, until ) { dir: function( elem, dir, until ) {
var matched = [], var matched = [],
// 存储匹配元素的数组
cur = elem[ dir ]; cur = elem[ dir ];
// 从当前元素的指定方向开始
// 遍历DOM树直到达到条件
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) { if ( cur.nodeType === 1 ) {
// 如果是元素节点,则添加到匹配数组中
matched.push( cur ); matched.push( cur );
} }
cur = cur[dir]; cur = cur[dir];
// 继续遍历
} }
return matched; return matched;
// 返回匹配元素的数组
}, },
// 根据给定的结果索引和方向从当前元素开始查找第n个兄弟元素
nth: function( cur, result, dir, elem ) { nth: function( cur, result, dir, elem ) {
result = result || 1; result = result || 1;
// 默认结果为1
var num = 0; var num = 0;
// 计数器
// 遍历兄弟元素直到找到第result个元素
for ( ; cur; cur = cur[dir] ) { for ( ; cur; cur = cur[dir] ) {
if ( cur.nodeType === 1 && ++num === result ) { if ( cur.nodeType === 1 && ++num === result ) {
break; break;
@ -5973,88 +6050,118 @@ jQuery.extend({
} }
return cur; return cur;
// 返回找到的元素
}, },
// 获取当前元素的所有兄弟元素(不包括自己),根据给定的起始节点
sibling: function( n, elem ) { sibling: function( n, elem ) {
var r = []; var r = [];
// 存储兄弟元素的数组
// 遍历所有兄弟元素
for ( ; n; n = n.nextSibling ) { for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) { if ( n.nodeType === 1 && n !== elem ) {
// 如果是元素节点且不是当前元素,则添加到数组中
r.push( n ); r.push( n );
} }
} }
return r; return r;
// 返回兄弟元素的数组
} }
}); });
// Implement the identical functionality for filter and not // 实现filter和not的相同功能根据给定的元素集合、条件函数和保留/排除标志来筛选元素
function winnow( elements, qualifier, keep ) { function winnow( elements, qualifier, keep ) {
// Can't pass null or undefined to indexOf in Firefox 4 // 确保qualifier不是null或undefined否则Firefox 4的indexOf会报错
// Set to 0 to skip string check // 设置为0以跳过字符串检查
qualifier = qualifier || 0; qualifier = qualifier || 0;
// 如果qualifier是一个函数则对每个元素执行该函数并根据返回值和keep标志筛选元素
if ( jQuery.isFunction( qualifier ) ) { if ( jQuery.isFunction( qualifier ) ) {
return jQuery.grep(elements, function( elem, i ) { return jQuery.grep(elements, function( elem, i ) {
var retVal = !!qualifier.call( elem, i, elem ); var retVal = !!qualifier.call( elem, i, elem );
return retVal === keep; return retVal === keep;
}); });
// 如果qualifier是一个DOM节点则筛选与qualifier相同的元素
} else if ( qualifier.nodeType ) { } else if ( qualifier.nodeType ) {
return jQuery.grep(elements, function( elem, i ) { return jQuery.grep(elements, function( elem, i ) {
return ( elem === qualifier ) === keep; return ( elem === qualifier ) === keep;
}); });
// 如果qualifier是一个字符串则首先筛选出所有元素节点
// 然后根据字符串是简单选择器还是复杂选择器来进一步筛选
} else if ( typeof qualifier === "string" ) { } else if ( typeof qualifier === "string" ) {
var filtered = jQuery.grep(elements, function( elem ) { var filtered = jQuery.grep(elements, function( elem ) {
return elem.nodeType === 1; return elem.nodeType === 1;
}); });
if ( isSimple.test( qualifier ) ) { if ( isSimple.test( qualifier ) ) {
// 如果是简单选择器
return jQuery.filter(qualifier, filtered, !keep); return jQuery.filter(qualifier, filtered, !keep);
} else { } else {
// 如果是复杂选择器
qualifier = jQuery.filter( qualifier, filtered ); qualifier = jQuery.filter( qualifier, filtered );
} }
} }
// 使用jQuery.grep和inArray来筛选元素根据元素是否在qualifier数组中以及keep标志
return jQuery.grep(elements, function( elem, i ) { return jQuery.grep(elements, function( elem, i ) {
return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
}); });
} }
function createSafeFragment( document ) { // 创建一个安全的文档片段用于插入DOM元素
// 这个方法通过创建一系列的元素来避免某些浏览器的安全限制
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ), var list = nodeNames.split( "|" ),
// 获取需要创建的元素名称列表
safeFrag = document.createDocumentFragment(); safeFrag = document.createDocumentFragment();
// 创建一个文档片段
// 如果文档片段支持createElement方法
if ( safeFrag.createElement ) { if ( safeFrag.createElement ) {
while ( list.length ) { while ( list.length ) {
safeFrag.createElement( safeFrag.createElement(
// 对列表中的每个元素名称,创建一个元素并添加到文档片段中
list.pop() list.pop()
); );
} }
} }
return safeFrag; return safeFrag;
} // 返回安全的文档片段
}
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + // 定义一组特定的HTML标签名这些标签名将被用于后续的正则表达式匹配
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
// 匹配jQuery版本号例如 jQuery123456789="123456" 或 jQuery123456789="null"
rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
// 匹配字符串开头的空白字符
rleadingWhitespace = /^\s+/, rleadingWhitespace = /^\s+/,
// 匹配自闭合的HTML标签但排除了area, br, col, embed, hr, img, input, link, meta, param这些标签
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
// 匹配HTML标签名
rtagName = /<([\w:]+)/, rtagName = /<([\w:]+)/,
// 匹配<tbody>标签
rtbody = /<tbody/i, rtbody = /<tbody/i,
// 匹配HTML标签或HTML实体
rhtml = /<|&#?\w+;/, rhtml = /<|&#?\w+;/,
// 匹配<script>或<style>标签
rnoInnerhtml = /<(?:script|style)/i, rnoInnerhtml = /<(?:script|style)/i,
// 匹配可能不需要缓存处理的标签,如<script>, <object>, <embed>, <option>, <style>
rnocache = /<(?:script|object|embed|option|style)/i, rnocache = /<(?:script|object|embed|option|style)/i,
// 匹配不需要shim缓存处理的特定标签
rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
// checked="checked" or checked // 匹配checked属性无论是否带有值
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
// 匹配JavaScript或ECMAScript脚本标签
rscriptType = /\/(java|ecma)script/i, rscriptType = /\/(java|ecma)script/i,
// 匹配注释或CDATA段的开始
rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/, rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
// 定义元素包装映射,用于将某些元素包裹在特定的父元素中
wrapMap = { wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ], option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ], legend: [ 1, "<fieldset>", "</fieldset>" ],
@ -6065,18 +6172,22 @@ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figca
area: [ 1, "<map>", "</map>" ], area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ] _default: [ 0, "", "" ]
}, },
// 创建一个安全的文档片段用于操作DOM
safeFragment = createSafeFragment( document ); safeFragment = createSafeFragment( document );
wrapMap.optgroup = wrapMap.option; // 为一些特殊标签设置相同的包装规则
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.optgroup = wrapMap.option;
wrapMap.th = wrapMap.td; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
// IE can't serialize <link> and <script> tags normally // 如果浏览器不支持HTML序列化则使用div作为默认包装元素
if ( !jQuery.support.htmlSerialize ) { if ( !jQuery.support.htmlSerialize ) {
wrapMap._default = [ 1, "div<div>", "</div>" ]; wrapMap._default = [ 1, "div<div>", "</div>" ];
} }
jQuery.fn.extend({ // 扩展jQuery的fn对象添加一些新的方法
jQuery.fn.extend({
// 设置或获取匹配元素的文本内容
text: function( value ) { text: function( value ) {
return jQuery.access( this, function( value ) { return jQuery.access( this, function( value ) {
return value === undefined ? return value === undefined ?
@ -6085,6 +6196,7 @@ jQuery.fn.extend({
}, null, value, arguments.length ); }, null, value, arguments.length );
}, },
// 将所有匹配元素包裹在一个HTML结构中
wrapAll: function( html ) { wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) { if ( jQuery.isFunction( html ) ) {
return this.each(function(i) { return this.each(function(i) {
@ -6093,13 +6205,14 @@ jQuery.fn.extend({
} }
if ( this[0] ) { if ( this[0] ) {
// The elements to wrap the target around // 克隆html结构并插入到DOM中
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
if ( this[0].parentNode ) { if ( this[0].parentNode ) {
wrap.insertBefore( this[0] ); wrap.insertBefore( this[0] );
} }
// 将匹配元素插入到克隆的html结构中
wrap.map(function() { wrap.map(function() {
var elem = this; var elem = this;
@ -6114,6 +6227,7 @@ jQuery.fn.extend({
return this; return this;
}, },
// 将匹配元素的内部内容包裹在一个HTML结构中
wrapInner: function( html ) { wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) { if ( jQuery.isFunction( html ) ) {
return this.each(function(i) { return this.each(function(i) {
@ -6126,30 +6240,37 @@ jQuery.fn.extend({
contents = self.contents(); contents = self.contents();
if ( contents.length ) { if ( contents.length ) {
// 如果元素有子节点则将子节点包裹在html结构中
contents.wrapAll( html ); contents.wrapAll( html );
} else { } else {
// 如果没有子节点则直接追加html
self.append( html ); self.append( html );
} }
}); });
}, },
// 将每个匹配元素包裹在一个HTML结构中
wrap: function( html ) { wrap: function( html ) {
var isFunction = jQuery.isFunction( html ); var isFunction = jQuery.isFunction( html );
return this.each(function(i) { return this.each(function(i) {
// 对每个元素执行包装操作如果html是函数则传入当前元素的索引并调用函数
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
}); });
}, },
// 移除匹配元素的父元素,但保留元素本身及其子元素
unwrap: function() { unwrap: function() {
return this.parent().each(function() { return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) { if ( !jQuery.nodeName( this, "body" ) ) {
// 如果不是body元素则用其子节点替换该元素
jQuery( this ).replaceWith( this.childNodes ); jQuery( this ).replaceWith( this.childNodes );
} }
}).end(); }).end();
}, },
// 在匹配元素的末尾追加内容
append: function() { append: function() {
return this.domManip(arguments, true, function( elem ) { return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) { if ( this.nodeType === 1 ) {
@ -6158,6 +6279,7 @@ jQuery.fn.extend({
}); });
}, },
// 在匹配元素的前面追加内容
prepend: function() { prepend: function() {
return this.domManip(arguments, true, function( elem ) { return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) { if ( this.nodeType === 1 ) {

Loading…
Cancel
Save