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选择器的主要内容,如果未能解决你的问题,请参考以下文章

jQuery选择器探究:Sizzle构造函数

jQuery选择器探究:Sizzle构造函数

jQuery选择器探究:语法汇总

jQuery选择器探究:语法汇总

jQuery选择器和遍历的总结

对于jQuery选择器和动画效果停止动画的实战心得前端jQuery框架