jQuery选择器探究:TAG选择器和CLASS选择器
Posted 程序猿子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jQuery选择器探究:TAG选择器和CLASS选择器相关的知识,希望对你有一定的参考价值。
(jquery1.6.1版本)假设有一个html文档中存在多个<h1>标签元素,那么当我们敲入$("h1")后在jQuery内部将会执行怎样的逻辑呢?分析jQuery构造函数我们同样定位到find( selector )方法,这个方法是jQuery的实例方法,代码如下:(5137行)
find: function( selector )
var self = this,
i, l;
if ( typeof selector !== "string" )
return jQuery( selector ).filter(function()
for ( i = 0, l = self.length; i < l; i++ )
if ( jQuery.contains( self[ i ], this ) )
return true;
);
var ret = this.pushStack( "", "find", selector ),
length, n, r;
for ( i = 0, l = this.length; i < l; i++ )
length = ret.length;
jQuery.find( selector, this[i], ret );
if ( i > 0 )
// Make sure that the results are unique
for ( n = length; n < ret.length; n++ )
for ( r = 0; r < length; r++ )
if ( ret[r] === ret[n] )
ret.splice(n--, 1);
break;
return ret;
,
我们暂不考虑其他调用的场景,只专注于selector参数为string类型时的逻辑。整体上调用流程是遵循jQuery一致的风格:实例方法"find(selector)"调用类方法"jQuery.find( selector, this[i], ret );",类方法find直接指向Sizzle构造函数"jQuery.find = Sizzle;"。当然还有一些其他的逻辑,如返回对象的构造、去重过滤等。
这时需要定位到Sizzle的构造函数,这时问题来了,Sizzle的定义不止一处,一处(3706行)定义是通用的,也是传统的,逻辑较为复杂,一处(4807行)定义在document.querySelectorAll存在时重载并复用传统定义。本文分析的是重载的相对简单的Sizzle定义。
Sizzle = function( query, context, extra, seed )
context = context || document;
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
if ( !seed && !Sizzle.isXML(context) )
// See if we find a selector to speed up
var match = /^(\\w+$)|^\\.([\\w\\-]+$)|^#([\\w\\-]+$)/.exec( query );
if ( match && (context.nodeType === 1 || context.nodeType === 9) )
// Speed-up: Sizzle("TAG")
if ( match[1] )
return makeArray( context.getElementsByTagName( query ), extra );
// Speed-up: Sizzle(".CLASS")
else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName )
return makeArray( context.getElementsByClassName( match[2] ), extra );
if ( context.nodeType === 9 )
// Speed-up: Sizzle("body")
// The body element only exists once, optimize finding it
if ( query === "body" && context.body )
return makeArray( [ context.body ], extra );
// Speed-up: Sizzle("#ID")
else if ( match && match[3] )
var elem = context.getElementById( match[3] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode )
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id === match[3] )
return makeArray( [ elem ], extra );
else
return makeArray( [], extra );
try
return makeArray( context.querySelectorAll(query), extra );
catch(qsaError)
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" )
var oldContext = context,
old = context.getAttribute( "id" ),
nid = old || id,
hasParent = context.parentNode,
relativeHierarchySelector = /^\\s*[+~]/.test( query );
if ( !old )
context.setAttribute( "id", nid );
else
nid = nid.replace( /'/g, "\\\\$&" );
if ( relativeHierarchySelector && hasParent )
context = context.parentNode;
try
if ( !relativeHierarchySelector || hasParent )
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
catch(pseudoError)
finally
if ( !old )
oldContext.removeAttribute( "id" );
return oldSizzle(query, context, extra, seed);
;
在传递给Sizzle函数的四个参数( query, context, extra, seed )中,当context参数和seed参数满足条件"if ( !seed && !Sizzle.isXML(context) )"时将执行重载的Sizzle逻辑,否则执行传统Sizzle逻辑。这里同样用一个正则表达式对象"/^(\\w+$)|^\\.([\\w\\-]+$)|^#([\\w\\-]+$)/"对选择符参数query执行匹配,这个正则对象有三个分组,分别捕获tag/class/id,也就是说重载的Sizzle函数功能上支持这三种最基本的选择器(尽管实际中#id选择符在jQuery构造函数中已经处理过了,理论上走不到这个地方来)。
当分组一存在时,执行tag选择器逻辑;当分组二存在时,执行class选择器逻辑。可以看到,对于这两种选择符,背后的逻辑回到原生dom的基本api上了:context.getElementsByTagName( query )、context.getElementsByClassName( match[2] ),当然,就像id选择符一样,Sizzle返回的对象集合肯定也不会是原始dom元素集合,所以makeArray函数的功能就是把选择出来的原生dom元素集合inject到jQuery对象extra(第三个参数)中去:(4561行)
var makeArray = function( array, results )
array = Array.prototype.slice.call( array, 0 );
if ( results )
results.push.apply( results, array );
return results;
return array;
;
makeArray函数的逻辑比较简单:接受两个参数,第一个是待处理array like对象,第二个是待扩展对象,不传递第二个对象时,直接将array like对象转换为真正的array对象,否则将转换后的真正的array对象inject到第二个参数jQuery对象中去。
简单的单纯的TAG选择器和CLASS选择器执行逻辑就是这样的,相对而言还是比较简单的,后续分析复杂的组合的选择器执行逻辑,也即真正的Sizzle核心。
以上是关于jQuery选择器探究:TAG选择器和CLASS选择器的主要内容,如果未能解决你的问题,请参考以下文章