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

3.10 工具方法

3.10.1 Sizzle.uniqueSort( 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     sortOrder = function( a, b ) {

4823         // The nodes are identical, we can exit early

4824         if ( a === b ) {

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         while ( cur ) {

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         if ( a === b ) {

4884             return ret;

4885         }

4886

4887         var cur = a.nextSibling;

4888

4889         while ( cur ) {

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 ( document.documentElement.compareDocumentPosition ) {

5248     Sizzle.contains = function( a, b ) {

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行:最后返回合并后的文本内容。

上一篇:蚂蚁金服西亭:智能金融的技术挑战与方案


下一篇:“聆听”升级,阿里云智能喜迎首席聆听官