<b>3.12 jquery扩展</b>
<b>3.12.1 暴露sizzle给jquery</b>
下面的代码将sizzle的方法和属性暴露给了jquery:
5288 // expose
5289 // override sizzle
attribute retrieval
5290 sizzle.attr =
jquery.attr;
5291 sizzle.selectors.attrmap
= {};
5292 jquery.find =
sizzle;
5293 jquery.expr =
sizzle.selectors;
5294 jquery.expr[":"]
= jquery.expr.filters;
5295 jquery.unique =
sizzle.uniquesort;
5296 jquery.text =
sizzle.gettext;
5297 jquery.isxmldoc =
sizzle.isxml;
5298 jquery.contains =
sizzle.contains;
3.12.2 .find( selector )
方法.find( selector )用于获取匹配元素集合中的每个元素的后代元素,可以用一个选择器表达式、jquery对象或dom元素来过滤查找结果,并用匹配的元素构造一个新的jquery对象作为返回值。该方法通过调用函数 sizzle( selector, context, results, seed )来实现,并在后者的基础上增加了对多上下文和链式语法的支持。
方法.find( selector )执行的2个关键步骤如下:
1)如果参数select是jquery对象或dom元素,则检查其是否是当前元素集合中某个元素的后代元素,是则保留,不是则丢弃。
2)如果参数selector是字符串,则遍历当前元素集合,以每个元素为上下文,调用方法jquery.find( selector, context, results, seed )也就是sizzle(
selector, context, results, seed ),查找匹配的后代元素,并将查找结果合并、去重。
相关代码如下所示:
5319 jquery.fn.extend({
5320
find: function( selector ) {
5321
var self = this,
5322 i, l;
5323
5324
if ( typeof selector !== "string" ) {
5325 return jquery( selector
).filter(function() {
5326 for ( i = 0, l = self.length;
i < l; i++ ) {
5327 if ( jquery.contains(
self[ i ], this ) ) {
5328 return true;
5329 }
5330 }
5331 });
5332
}
5333
5334
var ret = this.pushstack( "", "find", selector ),
5335 length, n, r;
5336
5337
for ( i = 0, l = this.length; i < l; i++ ) {
5338
length = ret.length;
5339 jquery.find( selector, this[i],
ret );
5340
5341 if ( i > 0 ) {
5342 // make sure that the results
are unique
5343 for ( n = length; n <
ret.length; n++ ) {
5344 for ( r = 0; r < length;
r++ ) {
5345 if ( ret[r] === ret[n]
) {
5346 ret.splice(n--,
1);
5347 break;
5348 }
5349 }
5350 }
5351 }
5352
5353
5354
return ret;
5355
},
5470 });
第5320行:定义方法.find( selector ),参数selector可以是选择器表达式,也可以是jquery对象或dom元素。
第5324~5332行:如果参数selector不是字符串,则认为该参数是jquery对象或dom元素,此时先将该参数统一封装为一个新jquery对象,然后遍历新jquery对象,检查其中的元素是否是当前jquery对象中某个元素的后代元素,如果是则保留,不是则丢弃。最后返回含有新jquery对象子集的另一个新jquery对象。
第5334行:调用方法.pushstack( elements, name, arguments )构造一个新的空jquery对象,并将其作为返回值,后面找到的元素都将被添加到该jquery对象中。
第5337~5352行:如果参数selector是字符串,则遍历当前元素集合,以每个元素为上下文,调用方法jquery.find( selector, context, results, seed )也就是sizzle(
第5341~5351行:从当前元素集合的第2个元素开始,遍历新找到的元素数组,移除其中与已找到的元素相等的元素,以确保找到的元素是唯一的。变量length表示已找到的元素集合的长度,也就是新找到的元素被插入的开始位置;移除时通过将循环变量n自减1,确保接下来的遍历不会漏掉元素。
3.12.3 .has( target )
方法.has( target )用当前jquery对象的子集构造一个新jquery对象,其中只保留子元素可以匹配参数target的元素。参数target可以是选择器表达式、jquery对象或dom元素。该方法通过调用方法.filter( selector )遍历当前匹配元素集合,通过调用jquery.contains( a, b ),也就是sizzle.contains(
a, b ),检查匹配元素是否包含了可以匹配参数target的子元素。
5357
has: function( target ) {
5358
var targets = jquery( target );
5359
return this.filter(function() {
5360 for ( var i = 0, l = targets.length;
5361 if ( jquery.contains( this,
targets[i] ) ) {
5362 return true;
5363 }
5364 }
5365
});
5366
第5357行:定义方法.has( target ),参数target可以是选择器表达式、jquery对象及元素。
第5358行:构造匹配参数target的jquery对象。
第5359~5365行:调用方法.filter( selector )遍历当前匹配元素集合,检查每个匹配元素是否包含了参数target所匹配的某个元素,如果包含则保留,不包含则丢弃。
3.12.4 .not( selector )、.filter(
selector )
方法.not( selector )用当前jquery对象的子集构造一个新jquery对象,其中只保留与参数selector不匹配的元素。参数selector可以是选择器表达式、jquery对象、函数、dom元素或dom元素数组。
方法.filter( selector )用当前jquery对象的子集构造一个新jquery对象,其中只保留与参数selector匹配的元素。参数selector可以是选择器表达式、jquery对象、函数、dom元素或dom元素数组。
方法.not( selector )和.filter( selector )通过调用函数winnow( elements, qualifier, keep )对当前匹配元素集合进行过滤,并用其返回值构造一个新jquery对象。不过,这两个方法的过滤行为正好相反,这种差异通过调用函数winnow( elements, qualifier, keep )时传入不同的参数keep来实现。
5368
not: function( selector ) {
5369
return this.pushstack( winnow(this, selector, false), "not",
selector );
5370
5371
5372
filter: function( selector ) {
5373
return this.pushstack( winnow(this, selector, true), "filter",
5374
5470 });
5590 // implement the
identical functionality for filter and not
5591 function winnow(
elements, qualifier, keep ) {
5592
5593
// can't pass null or undefined to indexof in firefox 4
5594
// set to 0 to skip string check
5595
qualifier = qualifier || 0;
5596
5597
if ( jquery.isfunction( qualifier ) ) {
5598
return jquery.grep(elements, function( elem, i ) {
5599 var retval = !!qualifier.call(
elem, i, elem );
5600 return retval === keep;
5601
});
5602
5603
} else if ( qualifier.nodetype ) {
5604
5605 return ( elem === qualifier ) ===
keep;
5606
5607
5608
} else if ( typeof qualifier === "string" ) {
5609
var filtered = jquery.grep(elements, function( elem ) {
5610 return elem.nodetype === 1;
5611
5612
5613
if ( issimple.test( qualifier ) ) {
5614 return jquery.filter(qualifier,
filtered, !keep);
5615
} else {
5616 qualifier = jquery.filter(
qualifier, filtered );
5617
5618
5619
5620
5621
return ( jquery.inarray( elem, qualifier ) >= 0 ) === keep;
5622 });
5623 }
第5591行:函数winnow( elements, qualifier, keep )负责过滤元素集合,它接受3个参数:
参数elements:待过滤的元素集合。
参数qualifier:用于过滤元素集合elements,可选值有函数、dom元素、选择器表达式、dom元素数组、jquery对象。
参数keep:布尔值。如果为true,则保留匹配元素,如果为false,则保留不匹配元素。
第5597~5601行:如果参数qualifier是函数,则调用方法jquery.grep( array, function
( elementofarray, indexinarray )[, invert]
)遍历元素集合elements,在每个元素上执行该函数,然后将返回值与参数keep进行比较,如果一致则保留,不一致则丢弃。
第5603~5606行:如果参数qualifier是dom元素,则调用方法jquery.grep( array, function( elementofarray, indexinarray )[,
invert] )遍历元素集合elements,检查其中的每个元素是否与参数qualifier相等,然后将检查结果与参数keep进行比较,如果一致则保留,不一致则丢弃。
第5608~5622行:如果参数qualifier是字符串,则先过滤出与该参数匹配的元素集合,然后调用方法jquery.grep( array, function( elementofarray, indexinarray )[,
invert] )遍历元素集合elements,检查其中的每个元素是否在过滤结果中,然后将检查结果与参数keep进行比较,如果一致则保留,不一致则丢弃。
第5620~5622行:如果参数qualifier是dom元素数组或jquery对象,则调用方法jquery.grep( array, function(elementofarray, indexinarray)[, invert]
)遍历元素集合 elements,检查其中的每个元素是否在参数qualifier中,然后将检查结果与参数keep进行比较,如果一致则保留,不一致则丢弃。
关于方法jquery.grep( array, function(elementofarray, indexinarray)[, invert]
)的介绍和分析请参见2.8.8节。
3.12.5 .is( selector )
方法.is( selector )用选择器表达式、dom元素、jquery对象或函数来检查当前匹配元素集合,只要其中某个元素可以匹配给定的参数就返回true。
5310
pos = jquery.expr.match.pos,
5376
is: function( selector ) {
5377
return !!selector && (
5378 typeof selector ===
"string" ?
5379 // if this is a positional
selector, check membership in the returned set
5380 // so
$("p:first").is("p:last") won't return true for a doc with
two "p".
5381 pos.test( selector ) ?
5382 jquery( selector,
this.context ).index( this[0] ) >= 0 :
5383 jquery.filter( selector,
this ).length > 0 :
5384 this.filter( selector ).length > 0 );
5385
第5376行:定义方法.is( selector ),其中参数selector可以是选择器表达式、dom元素、jquery对象或函数。
第5378行、第5381行、第5382行:如果参数selector是字符串,并且含有位置伪类,则先调用构造函数jquery( selector[, context] )查找参数selector匹配的元素集合,然后检查当前匹配元素集合中的第一个元素是否在查找结果中,如果在结果中则返回true,如果不在则返回false。
第5378行、第5381行、第5383行:如果参数selector是字符串,并且不含有位置伪类,则调用方法jquery.filter( expr, elems, not )用参数selector过滤当前匹配元素集合,然后检查过滤结果中是否仍有元素,如果有则返回true,如果没有则返回false。
第5378行、第5384行:如果参数selector不是字符串,则可能是dom元素、jquery对象或函数,先调用方法.filter( selector )用参数selector过滤当前匹配元素集合,然后检查过滤结果中是否仍有元素,如果有则返回true,如果没有则返回false。
3.12.6 .closest( selectors,
context )
方法.closest( selectors [, context] )用于在当前匹配元素集合和它们的祖先元素中查找与参数selectors匹配的最近元素,并用查找结果构造一个新jquery对象。
方法.closest( selectors [, context] )与.parents( [selector]
)的行为相似,都是沿着dom树向上查找匹配元素,需要注意的是两者的差别,具体如表3-16所示。
表3-16 .closest( selectors [,
context] )、.parents( [selector] )
序 号 .closest( selectors [,
context] ) .parents( [selector] )
1 从每个匹配元素开始 从每个匹配元素的父元素开始
2 沿着dom树向上遍历,直到找到匹配参数selectors的元素 沿着dom树向上遍历,查找所有与参数selectors匹配的元素
5387
closest: function( selectors, context ) {
5388
var ret = [], i, l, cur = this[0];
5389
5390
// array (deprecated as of jquery 1.7)
5391
if ( jquery.isarray( selectors ) ) {
5392 var level = 1;
5393
5394 while ( cur &&
cur.ownerdocument && cur !== context ) {
5395 for ( i = 0; i <
selectors.length; i++ ) {
5396
5397 if ( jquery( cur ).is(
selectors[ i ] ) ) {
5398 ret.push({ selector:
selectors[ i ], elem: cur, level: level });
5399 }
5400 }
5401
5402 cur = cur.parentnode;
5403 level++;
5404 }
5405
5406 return ret;
5407
5408
5409
// string
5410
var pos = pos.test( selectors ) || typeof selectors !==
5411 jquery( selectors, context ||
this.context ) :
5412 0;
5413
5414
5415 cur = this[i];
5416
5417 while ( cur ) {
5418 if ( pos ? pos.index(cur) >
-1 : jquery.find.matchesselector (cur, selectors) ) {
5419 ret.push( cur );
5420 break;
5421
5422 } else {
5423 cur = cur.parentnode;
5424 if ( !cur ||
!cur.ownerdocument || cur === context || cur.nodetype === 11 ) {
5425 break;
5426 }
5427 }
5428
}
5429
5430
5431
ret = ret.length > 1 ? jquery.unique( ret ) : ret;
5432
5433
return this.pushstack( ret, "closest", selectors );
5434
第5387行:定义方法.closest( selectors [, context] ),它接受2个参数:
参数selectors:用于匹配dom元素,可选值有:选择器表达式、jquery对象、dom元素、数组。
参数context:可选的上下文,用于限定查找范围。
第5391~5407行:如果参数selectors是数组,则从当前匹配元素集合中的第一个元素开始沿着dom树向上遍历,查找与数组selector中的元素匹配的元素,直到遇到上下文context或document对象为止。如果找到与数组selector中的元素匹配的元素,则放入数组ret中,其格式为:
{ selector: 参数 selector 中的元素, elem: 匹配元素, level: 向上查找的层级 }
第5414~5429行:遍历当前元素集合,在每个匹配元素和它的祖先元素中查找与参数selectors匹配的最近元素。
第5417~5428行:在向上遍历的过程中,如果找到了与参数selectors匹配的元素,则把它插入数组ret,然后跳出while循环,继续在下一个匹配元素和它的祖先元素中查找。
第5422~5427行:如果当前元素cur不匹配参数selectors,则读取它的父元素,继续向上遍历。如果父元素不存在,或者父元素不在文档中,或者父元素是上下文,或者父元素是文档片段,则跳出while循环,继续在下一个匹配元素和它的祖先元素中查找。
第5431行:如果找到了多个匹配参数selectors的元素,则调用方法jquery.unique( results ),也就是sizzle.uniquesort(
results ),执行排序和去重操作。
第5433行:最后用找到的元素构造一个新jquery对象,并返回。
3.12.7 .index( elem )
方法.index( elem )用于判断元素在元素集合中的下标位置。该方法的行为随参数elem的不同而不同,如表3-17所示。
表3-17 .index( elem )
序 号 参数 elem 行 为
1 没有传入 返回第一个匹配元素相对于其兄弟元素的下标位置
2 选择器表达式 返回第一个匹配元素在选择器表达式所匹配的元素集合中的位置
3 dom元素 返回dom元素在当前匹配元素集合中的位置
4 jquery对象 返回jquery对象的第一个元素在当前匹配元素集合中的位置
5436
// determine the position of an element within
5437
// the matched set of elements
5438
index: function( elem ) {
5439
5440
// no argument, return index in parent
5441
if ( !elem ) {
5442 return ( this[0] &&
this[0].parentnode ) ? this.prevall().length : -1;
5443
5444
5445
// index in selector
5446
if ( typeof elem === "string" ) {
5447 return jquery.inarray( this[0],
jquery( elem ) );
5448
5449
5450
// locate the position of the desired element
5451
return jquery.inarray(
5452 // if it receives a jquery object,
the first element is used
5453 elem.jquery ? elem[0] : elem, this
);
5454
},
第5441~5443行:如果没有参数,则返回当前匹配元素集合中第一个元素相对于其兄弟元素的位置。如果第一个元素没有父元素,则返回-1。
第5446~5448行:如果参数elem是字符串,则调用构造函数jquery( selector[, context ] )查找参数elem匹配的元素集合,然后调用方法jquery.inarray( elem, array, i )获取当前匹配元素集合中第一个元素在查找结果中的位置,并返回。
第5451~5453行:如果参数elem是dom元素,则调用方法jquery.inarray( elem, array, i )获取参数elem在当前元素匹配集合中的位置,并返回;如果参数elem是jquery对象,则调用方法jquery.inarray( elem, array, i )获取参数elem的第一个元素在当前匹配元素集合中的位置,并返回。
3.12.8 .add( selector,
方法.add( selector, context )用当前jquery对象中的元素和传入的参数构造一个新jquery对象。构造函数jquery()可以接受的参数格式,该方法都可以接受。另外,该方法不会改变当前jquery对象。
5456
add: function( selector, context ) {
5457
var set = typeof selector === "string" ?
5458 jquery( selector, context ) :
5459 jquery.makearray( selector
&& selector.nodetype ?
[ selector ] : selector ),
5460 all = jquery.merge( this.get(),
set );
5461
5462
return this.pushstack( isdisconnected( set[0] ) || isdisconnected
( all[0] ) ?
5463 all :
5464 jquery.unique( all ) );
5465
5472 // a painfully
simple check to see if an element is disconnected
5473 // from a document
(should be improved, where feasible).
5474 function
isdisconnected( node ) {
5475
return !node || !node.parentnode || node.parentnode.nodetype === 11;
5476 }
第5457~5459行:如果参数selector是字符串,则调用构造函数jquery( selector[, context] )查找与之匹配的元素集合,并赋值给变量set;否则调用方法jquery.makearray(
array, results )将参数selector转换为数组。
第5460行:调用方法jquery.merge( first, second )将当前jquery对象中的元素和元素集合set的元素合并,并赋值给变量all。
第5462~5464行:用合并后的元素集合all构造一个新jquery对象。如果元素集合set的第一个元素或合并后的元素集合all中第一个元素不在文档中,则可以直接用元素集合all构造新jquery对象;否则,需要先调用方法jquery.unique( results ),也就是sizzle.uniquesort(
results ),对合并后的元素集合all进行排序和去重,然后再构造jquery对象。
第5474~5476行:函数isdisconnected( node )用于简单地判断一个元素是否不在文档中。如果元素不存在,或者父元素不存在,或者父元素是文档片段,则返回true。
3.12.9 jquery.filter( expr,
elems, not )
方法jquery.filter( expr, elems, not )使用指定的选择器表达式expr对元素集合elems进行过滤,并返回过滤结果。如果参数not是true,则保留不匹配元素,否则默认保留匹配元素。
5540 jquery.extend({
5541
filter: function( expr, elems, not ) {
5542
if ( not ) {
5543 expr = ":not(" + expr +
")";
5544
5545
5546
return elems.length === 1 ?
5547
jquery.find.matchesselector(elems[0], expr) ? [ elems[0] ] : [] :
5548 jquery.find.matches(expr, elems);
5549
5588 });
第5541行:定义方法jquery.filter( expr, elems, not ),它接受3个参数:
参数expr:选择器表达式。
参数elems:待过滤的元素集合。
参数not:布尔值。如果是true,则保留不匹配元素,否则默认保留匹配元素。
第5542~5544行:修正选择器表达式expr。
第5546、5547行:如果元素集合elems中只有一个元素,则调用方法jquery.find.matches
selector( node, expr ),也就是sizzle.matchesselector(
node, expr ),来检查元素是否匹配选择器表达式expr。
第5546~5548行:如果含有多个元素,则调用方法jquery.find.matches( expr, set ),也就是sizzle.matches(
expr, set ),来用选择器表达式expr对元素集合elems进行过滤。
3.12.10 :animated
jquery的动画模块扩展了伪类:animated,用于检测dom元素是否正在执行动画。在对应的伪类过滤函数jquery.expr.filters.animated中,会遍历全局动画函数数组jquery.timers,检查每个动画函数的属性elem是否是当前元素;如果某个动画函数的属性elem是当前元素,则表示当前元素正在执行动画。更多信息请参考第14章。
8842 if ( jquery.expr
&& jquery.expr.filters ) {
8843
jquery.expr.filters.animated = function( elem ) {
8844
return jquery.grep(jquery.timers, function( fn ) {
8845 return elem === fn.elem;
8846
}).length;
8847
};
8848 }
3.12.11 hidden、:visible
jquery的样式操作模块扩展了伪类:hidden和:visible,用来判断dom元素是否占据布局空间。在对应的伪类过滤函数jquery.expr.filters.hidden/visible中,通过判断dom元素的可见宽度offsetwidth和可见高度offsetheight是否为0,来判断该元素是否占据布局空间。
注意:设置样式visibility为hidden,或设置样式opacity为0后,dom元素仍然会占据布局空间。
6816 if ( jquery.expr
6817
jquery.expr.filters.hidden = function( elem ) {
6818
var width = elem.offsetwidth,
6819 height = elem.offsetheight;
6820
6821
return ( width === 0 && height === 0 )
||
(!jquery.support.reliablehiddenoffsets && ((elem.style &&
elem.style.display) || jquery.css( elem, "display" )) ===
"none");
6822
6823
6824
jquery.expr.filters.visible = function( elem ) {
6825
return !jquery.expr.filters.hidden( elem );
6826
6827 }
第6817~6822行:如果dom元素的可见宽度offsetwidth和可见高度offsetheight是0,则认为该元素不占据布局空间。如果元素的可见高度offsetheight不可靠,则检查样式display;如果内联样式display是"none",则认为不占据布局空间;如果未设置行内样式display,但是计算样式display是"none",也认为不占据布局空间。
第6824~6826行:通过对伪类过滤函数jquery.expr.filters.hidden( elem )的结果取反,来确定元素是否占据布局空间。