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 <
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) & 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
&& 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 < al && i < 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 && (a.contains ? a.contains(b) : true);
5245
5246
5247 } else if (
5248
5249
return !!(a.comparedocumentposition(b) & 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行:最后返回合并后的文本内容。