天天看点

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.10 工具方法

3.10 工具方法

<b>3.10.1 sizzle.uniquesort(</b>

results )

工具方法sizzle.uniquesort( results )负责对元素集合中的元素按照出现在文档中的顺序进行排序,并删除重复元素。

相关代码如下所示:

4026 sizzle.uniquesort =

function( results ) {

4027    

if ( sortorder ) {

4028        

hasduplicate = basehasduplicate;

4029        

results.sort( sortorder );

4030

4031        

if ( hasduplicate ) {

4032             for ( var i = 1; i &lt;

results.length; i++ ) {

4033                 if ( results[i] === results[ i

- 1 ] ) {

4034                     results.splice( i--, 1 );

4035                 }

4036             }

4037        

}

4038    

4039

4040  

  return results;

4041 };

第4029行:调用数组方法sort()对数组中的元素进行排序。其中,sortorder( a, b )是比较函数,负责比较元素a和元素b在文档中的位置。如果比较函数sortorder( a, b )遇到相等的元素,即重复元素,会设置变量hasduplicate为true。关于比较函数sortorder( a, b )的具体说明请参见3.10.2节。

第4031~4037行:如果变量hasduplicate为true,表示存在重复元素,则遍历数组results,比较相邻元素是否相等,如果相等则删除。

第4028行:开始排序和去重时,先设置变量hasduplicate的默认值为变量basehas

duplicate,变量basehasduplicate指示了javascript引擎在排序时是否会进行优化。

3864    

hasduplicate = false,

3865    

basehasduplicate = true,

3870 // here we check if

the javascript engine is using some sort of

3871 // optimization

where it does not always call our comparision

3872 // function. if that

is the case, discard the hasduplicate value.

3873 // thus far that

includes google chrome.

3874 [0,

0].sort(function() {

3875    

basehasduplicate = false;

3876    

return 0;

3877 });

第3874~3877行:检查javascript引擎在排序时是否会进行优化。在早期的chrome浏览器中,排序时如果遇到相等的元素,不会调用比较函数,新版本中已经取消了这一优化。如果遇到相等元素便不调用比较函数,此时变量basehasduplicate默认为true,即只能假设数组中含有重复元素;如果遇到相等元素时仍然会调用比较函数,则变量basehasduplicate将被设置为false,这种情况下需要在比较函数中判断是否含有重复元素。

读者可以访问http://bugs.jquery.com/ticket/5380查看该bug的描述。

3.10.2 sortorder( a, b )

函数sortorder( a, b )负责比较元素a和元素b在文档中的位置。如果元素a在元素b之前,则返回-1;如果元素a在元素b之后,则返回1;如果元素a与元素b相等,则返回0。

函数sortorder( a, b )通过调用原生方法comparedocumentposition()或比较原生属性source

index来实现。原生方法comparedocumentposition()用于比较两个元素的文档位置;原生属性sourceindex则返回元素在文档中的序号,返回值等于该元素在document.getelementsby

tagname('*')返回的数组中的下标。更多信息请访问http://www.quirksmode.org/dom/w3c_core.html。

函数sortorder( a, b )执行的3个关键步骤如下:

1)如果浏览器支持原生方法comparedocumentposition(),则调用该方法比较元素位置。

2)如果浏览器支持原生属性sourceindex,则用该属性比较元素位置。

3)否则比较祖先元素的文档位置。

下面来看看该函数的源码实现。

1.?浏览器支持原生方法comparedocumentposition()的情况

如果浏览器支持原生方法comparedocumentposition(),则调用该方法比较元素位置。相关代码如下所示:

4805 var sortorder,

siblingcheck;

4806

4807 if (

document.documentelement.comparedocumentposition ) {

4808    

sortorder = function( a, b ) {

4809       

if ( a === b ) {

4810            hasduplicate = true;

4811            return 0;

4812       

4813

4814       

if ( !a.comparedocumentposition || !b.comparedocumentposition ) {

4815           return a.comparedocumentposition ?

-1 : 1;

4816       

4817

4818       

return a.comparedocumentposition(b) &amp; 4 ? -1 : 1;

4819    

};

4820

2.?浏览器支持原生属性sourceindex的情况

如果浏览器支持原生属性sourceindex,则用该属性比较元素位置。

4821 } else {

4822    

4823        

// the nodes are identical, we can exit early

4824        

4825            hasduplicate = true;

4826            return 0;

4827

4828        

// fallback to using sourceindex (in ie) if it's available on both nodes

4829       

 } else if ( a.sourceindex

&amp;&amp; b.sourceindex ) {

4830             return a.sourceindex -

b.sourceindex;

4831        

4832

3.否则比较祖先元素的文档位置

(1)元素a和元素b是兄弟元素的情况

4833        

var al, bl,

4834             ap = [],

4835             bp = [],

4836    

        aup = a.parentnode,

4837             bup = b.parentnode,

4838             cur = aup;

4839

4840        

// if the nodes are siblings (or identical) we can do a quick check

4841        

if ( aup === bup ) {

4842             return siblingcheck( a, b );

4843

第4836~4837行、第4841~4842行:变量aup是元素a的父元素,变量bup是元素b的父元素。如果变量aup与变量bup相等,说明元素a和元素b是兄弟元素,则调用函数siblingcheck()比较元素a和元素b的文档位置。函数siblingcheck( a, b, ret )负责比较兄弟元素的文档位置,稍后会看到该函数的源码实现和分析。

(2)没有找到父元素的情况

4844        

// if no parents were found then the nodes are disconnected

4845        

} else if ( !aup ) {

4846             return -1;

4847

4848        

} else if ( !bup ) {

4849             return 1;

4850        

4851

第4845~4850行:如果元素a没有父元素,则认为元素a不在文档中,返回-1,元素a将排在元素b之前;如果元素b没有父元素,则认为元素b不在文档中,返回1,元素b将排在元素a之前。

(3)查找元素a和元素b的祖先元素

4852        

// otherwise they're somewhere else in the tree so we need

4853        

// to build up a full list of the parentnodes for comparison

4854        

while ( cur ) {

4855             ap.unshift( cur );

4856             cur = cur.parentnode;

4857        

4858

4859        

cur = bup;

4860

4861        

4862             bp.unshift( cur );

4863             cur = cur.parentnode;

4864        

4865

(4)比较祖先元素的文档位置

4866        

al = ap.length;

4867        

bl = bp.length;

4868

4869        

// start walking down the tree looking for a discrepancy

4870        

for ( var i = 0; i &lt; al &amp;&amp; i &lt; bl; i++ ) {

4871             if ( ap[i] !== bp[i] ) {

4872                 return siblingcheck( ap[i],

bp[i] );

4873             }

4874        

4875

第4866~4874行:从最顶层的祖先元素开始向下遍历,如果祖先元素ap[i]与bp[i]不是同一个元素,那么它们必然是兄弟元素,此时可以通过比较两个祖先元素的文档位置,来确定元素a和元素b的相对位置。

(5)元素a和元素b的文档深度不一致的情况

4876        

// we ended someplace up the tree so do a sibling check

4877        

return i === al ?

4878             siblingcheck( a, bp[i], -1 ) :

4879             siblingcheck( ap[i], b, 1 );

4880    

4881

第4877~4878行:如果元素a的文档深度较小,此时元素a与元素b的祖先元素bp[i]要么是兄弟元素,要么是同一个元素,可以调用函数siblingcheck( a, b, ret )比较文档位置。

第4877~4879行:如果元素b的文档深度较小,此时元素a的祖先元素ap[i]与元素b要么是兄弟元素,要么是同一个元素,可以调用函数siblingcheck( a, b, ret )比较文档位置。

(6)siblingcheck( a, b, ret )

函数siblingcheck( a, b, ret )负责比较兄弟元素的文档位置。该函数从元素a向后遍历(nextsibling),如果遇到元素b,说明元素a在元素b之前,则返回-1;如果一直没遇到,说明元素a在元素b之后,则返回1。

4882    

siblingcheck = function( a, b, ret ) {

4883        

4884             return ret;

4885        

4886

4887        

var cur = a.nextsibling;

4888

4889        

4890             if ( cur === b ) {

4891                 return -1;

4892             }

4893

4894             cur = cur.nextsibling;

4895        

4896

4897        

return 1;

4898    

4899 }

3.10.3 sizzle.contains( a,

b )

工具方法sizzle.contains( a, b )负责检测元素a是否包含元素b。该方法通过调用原生方法contains()或comparedocumentposition()实现。原生方法contains()用于检测一个元素是否包含另一个元素;原生方法comparedocumentposition()用于比较两个元素的文档位置,更多信息请访问以下网址:

http://ejohn.org/blog/comparing-document-position/

http://www.quirksmode.org/dom/w3c_core.html#miscellaneous

5242 if (

document.documentelement.contains ) {

5243    

sizzle.contains = function( a, b ) {

5244       

return a !== b &amp;&amp; (a.contains ? a.contains(b) : true);

5245    

5246

5247 } else if (

5248    

5249       

return !!(a.comparedocumentposition(b) &amp; 16);

5250    

5251

5252 } else {

5253    

sizzle.contains = function() {

5254       

return false;

5255    

5256 }

3.10.4 sizzle.error( msg )

工具方法sizzle.error( msg )用于抛出一个含有选择器表达式语法错误信息的异常。

4178 sizzle.error =

function( msg ) {

4179    

throw new error( "syntax error, unrecognized expression: " +

msg );

4180 };

3.10.5 sizzle.gettext( elem

)

工具方法sizzle.gettext( elem )用于获取元素集合中所有元素合并后的文本内容。

4182 /**

4183 * utility function

for retreiving the text value of an array of dom nodes

4184 * @param

{array|element} elem

4185 */

4186 var gettext =

sizzle.gettext = function( elem ) {

4187    

var i, node,

4188        

nodetype = elem.nodetype,

4189        

ret = "";

4190

4191    

if ( nodetype ) {

4192        

if ( nodetype === 1 || nodetype === 9 ) {

4193             // use textcontent || innertext

for elements

4194             if ( typeof elem.textcontent ===

'string' ) {

4195                 return elem.textcontent;

4196             } else if ( typeof elem.innertext

=== 'string' ) {

4197                 // replace ie's carriage

returns

4198                 return elem.innertext.replace(

rreturn, '' );

4199             } else {

4200                 // traverse it's children

4201                 for ( elem = elem.firstchild;

elem; elem = elem.nextsibling) {

4202                     ret += gettext( elem );

4203                 }

4204             }

4205        

} else if ( nodetype === 3 || nodetype === 4 ) {

4206             return elem.nodevalue;

4207        

4208    

} else {

4209

4210        

// if no nodetype, this is expected to be an array

4211        

for ( i = 0; (node = elem[i]); i++ ) {

4212             // do not traverse comment nodes

4213             if ( node.nodetype !== 8 ) {

4214                 ret += gettext( node );

4215             }

4216        

4217    

4218    

return ret;

4219 };

第4191~4207行:如果参数elem是元素,则尝试读取属性textcontent或innertext,如果不支持则遍历子元素,递归调用工具函数gettext( elem )来获取每个子元素的文本内容,并合并;如果参数elem是text节点或cdatasection节点,则直接返回节点值nodevalue。

第4208~4217行:否则认为参数elem是元素集合,遍历该元素集合,递归调用函数gettext(elem)获取每个元素的文本内容,并合并。

第4218行:最后返回合并后的文本内容。

继续阅读