天天看點

jQuery技術内幕:深入解析jQuery架構設計與實作原理. 3.12 jQuery擴充

<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 &lt; 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 &lt; l; i++ ) {

5338 

           length = ret.length;

5339             jquery.find( selector, this[i],

ret );

5340

5341             if ( i &gt; 0 ) {

5342                 // make sure that the results

are unique

5343                 for ( n = length; n &lt;

ret.length; n++ ) {

5344                     for ( r = 0; r &lt; 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 ) &gt;= 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 &amp;&amp; (

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] ) &gt;= 0 :

5383                     jquery.filter( selector,

this ).length &gt; 0 :

5384                 this.filter( selector ).length &gt; 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 &amp;&amp;

cur.ownerdocument &amp;&amp; cur !== context ) {

5395                 for ( i = 0; i &lt;

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) &gt;

-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 &gt; 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] &amp;&amp;

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

&amp;&amp; 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

&amp;&amp; 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 &amp;&amp; height === 0 )

                 ||

(!jquery.support.reliablehiddenoffsets &amp;&amp; ((elem.style &amp;&amp;

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 )的結果取反,來确定元素是否占據布局空間。

繼續閱讀