`
mazhiyuan
  • 浏览: 62530 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jquery源码分析之属性篇

阅读更多

jquery提供了一些快捷函数来对dom对象的属性进行存取操作. 这一部分还是比较简单的. jquery的主要工作还是为了解决浏览器的兼容性. 这部分的方法一般都有2个特点.
1, set方法和get方法一体化. 根据参数数量来判断是set还是get.
2, value可以传入一个闭包. 这个闭包的返回值才是真正的value.

jQuery.prototype.attr.
实际上这个方法就是setAttribute和getAttribute操作的集合.jQuery把又长又难记的函数名省略成attr.

严格的讲, 挂在dom元素身上的属性,又可以分为属性(property)和特性(attribute).
属性可以看成是dom节点对象上的一个普通的property.跟var a = {b:1}的a.b一样. 设置它可以用a.b=1, 删除它可以用delete a.b
而特性是真正被应用到dom节点当中, 会改变节点在document中的状态.如果给dom节点添加了一个特性, 在firebug中是能够看到dom节点标签里多了这个attribute的.删除它要用removeAttribute

对于id, a标签的href这样的特殊属性,通过dom0方式(即xxx.xxx=xxx)和dom1方式(setAttribute/getAttribute)方式都可以访问到它们.这些特殊的属性也被当成特性, 在各个浏览器中行为都是一样的.

除了这些特殊属性, 在firefox等符合w3c标准的浏览器中,特性和属性是严格区分的. 如果要给dom节点设置/取得一个自定义特性,必须用setAttribute/getAttribute, 在firefox中用dom0的方式操作的只是这个dom节点对象的property.
而在IE中, dom节点内的任何通过硬编码(即写在html标签内的属性)和通过dom0方式(即xxx.xxx=xxx)设置的属性,也都被当做了特性. 也就是说set/get它的时候通过dom0方式和dom1方式都可以.

删除dom节点特性/属性的时候, 如果是特性就用removeAttrtibute, 如果是属性就用delete xxx.xxx. 由上面的分析可知, IE中都是用removeAttrtibute. 而firefox中要看具体到底是特性还是属性.因为各个浏览器对特性/属性的混乱实现, jquery里面提供了jQuery.support.deleteExpando这个特性检测.

了解了这些之后就来看看attr函数的实现.
暴露给api的原型方法非常简单,只有一句话.把参数交给jQuery.access函数去处理. jQuery.access主要作用是修正参数. 看看attr函数里的最后一个参数jQuery.attr. 这个参数的作用是告诉access方法, 修正完参数后再去调用 jQuery.attr方法. 因为access还可以被 jQuery.css利用,如果这里的最后一个参数是 jQuery.css.

Java代码 复制代码
  1. attr: function( name, value ) {   
  2.         return jQuery.access( this, name, value, true, jQuery.attr );   
  3.     }  
attr: function( name, value ) {
		return jQuery.access( this, name, value, true, jQuery.attr );
	}


access忘记是1.3还是1.4版本被提炼出来的.它的作用就好像机场的安检处,检查你的行李,没收你的打火机.然后根据你的机票告诉你,北京往左边走,深圳往右边走,当然还记得带上你的行李.

Java代码 复制代码
  1. access: function( elems, key, value, exec, fn, pass ) {   
  2. //value可能是一个闭包函数,exec参数是判断value是一个直接的//string值还是这个闭包函数计算后的返回值, 属性操作这部分的方法里都可以传入一个闭包的返回值当value.可以应用于   
  3. //$("div").attr("id", function(elem){return parseInt(elem.id) + 1})   
  4.         var length = elems.length;   
  5.         // Setting many attributes   
  6.         if ( typeof key === "object" ) {   
  7.      //可以传入object类型参数,一次设置多个属性.   
  8.             for ( var k in key ) {   
  9.                 jQuery.access( elems, k, key[k], exec, fn, value );   
  10.             }   
  11.             return elems;   
  12.         }   
  13.   
  14.         // set方式   
  15.         if ( value !== undefined ) {   
  16.             exec = !pass && exec && jQuery.isFunction(value);   
  17.         //pass默认是undefined的. 所以如果exec为true并且value   
  18.         //是函数的话, value应该是这个函数的返回值.   
  19.             for ( var i = 0; i < length; i++ ) {   
  20.         //给集合内的每个元素都设置key,value.   
  21.         //这里fn是jQuery.attr   
  22.                 fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );   
  23.             }   
  24.            
  25.             return elems;   
  26.         }   
  27.         // get方式   
  28.         return length ? fn( elems[0], key ) : undefined;   
  29.     }  
access: function( elems, key, value, exec, fn, pass ) {
//value可能是一个闭包函数,exec参数是判断value是一个直接的//string值还是这个闭包函数计算后的返回值, 属性操作这部分的方法里都可以传入一个闭包的返回值当value.可以应用于
//$("div").attr("id", function(elem){return parseInt(elem.id) + 1})
		var length = elems.length;
		// Setting many attributes
		if ( typeof key === "object" ) {
     //可以传入object类型参数,一次设置多个属性.
			for ( var k in key ) {
				jQuery.access( elems, k, key[k], exec, fn, value );
			}
			return elems;
		}

		// set方式
		if ( value !== undefined ) {
			exec = !pass && exec && jQuery.isFunction(value);
        //pass默认是undefined的. 所以如果exec为true并且value
        //是函数的话, value应该是这个函数的返回值.
			for ( var i = 0; i < length; i++ ) {
        //给集合内的每个元素都设置key,value.
        //这里fn是jQuery.attr
				fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
			}
		
			return elems;
		}
		// get方式
		return length ? fn( elems[0], key ) : undefined;
	}



access函数最后把参数又传递给了jQuery.attr, 在jQuery.attr里才真正进行setAttribute/getAttribute操作.

Java代码 复制代码
  1. attr: function( elem, name, value, pass ) {   
  2.         // don't set attributes on text and comment nodes   
  3.         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {   
  4.             return undefined;   
  5.         }   
  6.   
  7.         if ( pass && name in jQuery.attrFn ) {   
  8.             //jQuery.attrFn里包含了一些快捷方法, 如.html(), .text()   
  9.       //如果name等于html,text这些, 直接调用这些快捷方法.   
  10.             return jQuery(elem)[name](value);   
  11.         }   
  12.   
  13.         var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),   
  14.             //确认不是xml文档   
  15.             set = value !== undefined;   
  16.             //判断是set方式还是get方式   
  17.         name = notxml && jQuery.props[ name ] || name;   
  18.         //修正 name, 比如class变成className.   
  19.         if ( elem.nodeType === 1 ) {   
  20.             var special = rspecialurl.test( name );   
  21.             //如果name是 href || src || style, 特殊处理.   
  22.             if ( name === "selected" && !jQuery.support.optSelected ) {   
  23.                 //对safari中select的特殊处理   
  24.                 var parent = elem.parentNode;   
  25.                 if ( parent ) {   
  26.                     parent.selectedIndex;   
  27.        
  28.                     if ( parent.parentNode ) {   
  29.                         parent.parentNode.selectedIndex;   
  30.                     }   
  31.                 }   
  32.             }   
  33.   
  34.             if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {   
  35.                 //这个if分支是处理dom0方式. name in elem就可以判断出name是elem中一个已经存在的property,    
  36.                 //如果是这种情况, 实际上是通过dom0方式修改这个   
  37. //属性的值. 这次操作跟设置元素的特性无关.   
  38.                 //并且name不是style, href, src.   
  39.                 if ( set ) {   
  40.       if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {   
  41.                 //input和button的type属性不能被改变   
  42.         jQuery.error( "type property can't be changed" );   
  43.         }   
  44.   
  45.                     if ( value === null ) {   
  46.                         //value为null的话, 实际上remove掉这个特性.   
  47.                         if ( elem.nodeType === 1 ) {   
  48.                             elem.removeAttribute( name );   
  49.                         }   
  50.   
  51.                     } else {   
  52.                         elem[ name ] = value;   
  53.                         //用dom0的方式set   
  54.                     }   
  55.                 }   
  56.                 if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {   
  57.                     //表单元素返回的是nodeValue.   
  58.                     return elem.getAttributeNode( name ).nodeValue;   
  59.                 }   
  60.   
  61.                 if ( name === "tabIndex" ) {   
  62.          //如果tabIndex没有被显示设定, 可能得不到正确的值   
  63.         var attributeNode = elem.getAttributeNode( "tabIndex" );   
  64.                     return attributeNode && attributeNode.specified ?   
  65.                         attributeNode.value :   
  66.                         rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?   
  67.                             0 :   
  68.                             undefined;   
  69.                 }   
  70.   
  71.                 return elem[ name ];   
  72.                 //dom0方式, 返回属性值   
  73.             }   
  74.   
  75.             if ( !jQuery.support.style && notxml && name === "style" ) {   
  76.                 //如果是set/get style.   
  77.                 if ( set ) {   
  78.                     elem.style.cssText = "" + value;   
  79.                 }   
  80.   
  81.                 return elem.style.cssText;   
  82.             }   
  83.   
  84.             if ( set ) {   
  85.         //非dom0方式.   
  86.         //除了IE之外的浏览器, getAttribute的返回值都是string类型.    
  87.         //而IE中返回的是原始类型, 这里为了统一, setAttribute之前   
  88.       //先转化为string   
  89.                 elem.setAttribute( name, "" + value );   
  90.             }   
  91.   
  92.             // Ensure that missing attributes return undefined   
  93.             // Blackberry 4.7 returns "" from getAttribute #6938   
  94.             if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {   
  95.                 //没有这个attribute的话, 返回undefined.   
  96.                 return undefined;   
  97.             }   
  98.   
  99.             var attr = !jQuery.support.hrefNormalized && notxml && special ?   
  100.                     elem.getAttribute( name, 2 ) :   
  101.                     elem.getAttribute( name );   
  102. /*  
  103.     当dom节点的属性是一个超链接的时候, IE下面的反应不一样.  
  104.         比如如果href是一个相对路径.它会把你的域名自动补全到href前面再返回.不过IE和firefox都提供了一个返回  原始文本值的方法. 即elem.getAttribute( "href", 2 ).  
  105. */  
  106.             return attr === null ? undefined : attr;   
  107.         }   
  108.     }   
  109. })  
attr: function( elem, name, value, pass ) {
		// don't set attributes on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
			return undefined;
		}

		if ( pass && name in jQuery.attrFn ) {
			//jQuery.attrFn里包含了一些快捷方法, 如.html(), .text()
      //如果name等于html,text这些, 直接调用这些快捷方法.
			return jQuery(elem)[name](value);
		}

		var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
			//确认不是xml文档
			set = value !== undefined;
			//判断是set方式还是get方式
		name = notxml && jQuery.props[ name ] || name;
		//修正 name, 比如class变成className.
		if ( elem.nodeType === 1 ) {
			var special = rspecialurl.test( name );
			//如果name是 href || src || style, 特殊处理.
			if ( name === "selected" && !jQuery.support.optSelected ) {
				//对safari中select的特殊处理
				var parent = elem.parentNode;
				if ( parent ) {
					parent.selectedIndex;
	
					if ( parent.parentNode ) {
						parent.parentNode.selectedIndex;
					}
				}
			}

			if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
				//这个if分支是处理dom0方式. name in elem就可以判断出name是elem中一个已经存在的property, 
				//如果是这种情况, 实际上是通过dom0方式修改这个
//属性的值. 这次操作跟设置元素的特性无关.
				//并且name不是style, href, src.
				if ( set ) {
      if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
				//input和button的type属性不能被改变
		jQuery.error( "type property can't be changed" );
		}

					if ( value === null ) {
						//value为null的话, 实际上remove掉这个特性.
						if ( elem.nodeType === 1 ) {
							elem.removeAttribute( name );
						}

					} else {
						elem[ name ] = value;
						//用dom0的方式set
					}
				}
				if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
					//表单元素返回的是nodeValue.
					return elem.getAttributeNode( name ).nodeValue;
				}

				if ( name === "tabIndex" ) {
		 //如果tabIndex没有被显示设定, 可能得不到正确的值
		var attributeNode = elem.getAttributeNode( "tabIndex" );
					return attributeNode && attributeNode.specified ?
						attributeNode.value :
						rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
							0 :
							undefined;
				}

				return elem[ name ];
				//dom0方式, 返回属性值
			}

			if ( !jQuery.support.style && notxml && name === "style" ) {
				//如果是set/get style.
				if ( set ) {
					elem.style.cssText = "" + value;
				}

				return elem.style.cssText;
			}

			if ( set ) {
		//非dom0方式.
		//除了IE之外的浏览器, getAttribute的返回值都是string类型. 
		//而IE中返回的是原始类型, 这里为了统一, setAttribute之前
      //先转化为string
				elem.setAttribute( name, "" + value );
			}

			// Ensure that missing attributes return undefined
			// Blackberry 4.7 returns "" from getAttribute #6938
			if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
				//没有这个attribute的话, 返回undefined.
				return undefined;
			}

			var attr = !jQuery.support.hrefNormalized && notxml && special ?
					elem.getAttribute( name, 2 ) :
					elem.getAttribute( name );
/*
	当dom节点的属性是一个超链接的时候, IE下面的反应不一样.
        比如如果href是一个相对路径.它会把你的域名自动补全到href前面再返回.不过IE和firefox都提供了一个返回  原始文本值的方法. 即elem.getAttribute( "href", 2 ).
*/
			return attr === null ? undefined : attr;
		}
	}
})


再看看jQuery.prototype.removeAttr函数

Java代码 复制代码
  1. removeAttr: function( name, fn ) {   
  2.         return this.each(function(){   
  3.             jQuery.attr( this, name, "" ); ------(1)设置为""有什么意义??   
  4.             if ( this.nodeType === 1 ) {   
  5.                 this.removeAttribute( name );  -------------------(2)   
  6.             }   
  7.         });   
  8.     }  
removeAttr: function( name, fn ) {
		return this.each(function(){
			jQuery.attr( this, name, "" ); ------(1)设置为""有什么意义??
			if ( this.nodeType === 1 ) {
				this.removeAttribute( name );  -------------------(2)
			}
		});
	}


代码很直观, (1)是掉用jQuery.attr删除dom0方式注册的属性. 具体实现原理可以重新跟着走一次jQuery.attr方法.(2)处是删除特性. 不过我有个疑问. 假如有这样一段代码.
$("#div1")[0].fff = 111;
$("#div1").removeAttr("fff");
alert ($("#div1")[0].fff);  //firefox下是"",  IE下是undefined.
就我个人的理解, 这2个值是应该统一的. 为什么IE和firefox返回的值不一样, 因为这个通过dom0方式注册的属性fff, 在IE下也是元素的特性,而在firefox下则不然. (1)处调用的jQuery.attr()只负责完把属性设置为""之后就退出了. 而(2)处的代码只能删除特性,所以实际上只在IE下起作用, 所以remove掉之后只有IE弹出的是undefined. 其实在(2)处的后面加上这样一句代码就OK了.

Java代码 复制代码
  1. jQuery.support.deleteExpando && delete this[name];  
jQuery.support.deleteExpando && delete this[name];



看看几个跟元素的className有关的方法, 包括addClass, removeClass, toggleClass.

jQuery.prototype.addClass
先看看addClass方法, 给元素添加className. 元素可以同时有多个className. 每2个className中间以空格符来分开.

这个方法的思路很简单. 先取出原来的className, 再用新来的className和旧的className做比较. 旧的className中没有它, 就合并到原来的className字符串中.

Java代码 复制代码
  1. addClass: function( value ) {     
  2.         if ( jQuery.isFunction(value) ) {     
  3.     //如果value是一个函数   
  4.             return this.each(function(i) {   
  5.                 var self = jQuery(this);   
  6.                 self.addClass( value.call(this, i, self.attr("class")) );    
  7.     //把函数的返回值当做参数重新调用addClass函数   
  8.             });   
  9.         }   
  10.   
  11.         if ( value  &&  typeof value === "string" ) {       
  12.             var classNames = (value || "").split( rspace );     
  13.     //以空格为分割号, 把参数make成数组   
  14.   
  15.             for ( var i = 0, l = this.length; i < l; i++ ) {     
  16.                 //循环集合内每个元素, 依次addClass   
  17.     var elem = this[i];   
  18.                 if ( elem.nodeType === 1 ) {     
  19.     //确认是dom节点   
  20.                     if ( !elem.className ) {      
  21.                     elem.className = value;   
  22.     //如果没有className属性, 直接把className设置为value   
  23.                     } else {   
  24.                         var className = " " + elem.className + " ", setClass = elem.className;     
  25.     //加2个空格是为了统一className的格式, 方便后面的indexOf判断   
  26.                         for ( var c = 0, cl = classNames.length; c < cl; c++ ) {   
  27.                             if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {    
  28.     //如果当前的className中没有   
  29.                                 setClass += " " + classNames[c];          
  30.                             }   
  31.                         }   
  32.                         elem.className = jQuery.trim( setClass );     
  33.     //去掉前后空格   
  34.                     }   
  35.                 }   
  36.             }   
  37.         }   
  38.         return this;   
  39.     }  
addClass: function( value ) {  
		if ( jQuery.isFunction(value) ) {  
	//如果value是一个函数
			return this.each(function(i) {
				var self = jQuery(this);
				self.addClass( value.call(this, i, self.attr("class")) ); 
	//把函数的返回值当做参数重新调用addClass函数
			});
		}

		if ( value  &&  typeof value === "string" ) {    
			var classNames = (value || "").split( rspace );  
	//以空格为分割号, 把参数make成数组

			for ( var i = 0, l = this.length; i < l; i++ ) {  
				//循环集合内每个元素, 依次addClass
	var elem = this[i];
				if ( elem.nodeType === 1 ) {  
	//确认是dom节点
					if ( !elem.className ) {   
					elem.className = value;
	//如果没有className属性, 直接把className设置为value
					} else {
						var className = " " + elem.className + " ", setClass = elem.className;  
	//加2个空格是为了统一className的格式, 方便后面的indexOf判断
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { 
	//如果当前的className中没有
								setClass += " " + classNames[c];       
							}
						}
						elem.className = jQuery.trim( setClass );  
	//去掉前后空格
					}
				}
			}
		}
		return this;
	}


jQuery.prototype.removeClass

删除元素的某个className
removeClass也是用旧的className字符串和参数比较. 如果有参数在这个字符串中, 说明元素包含了这个className. 把className中的这一段用空格replace掉. 如果参数为undefined. 清空元素所有的className,实际上就是直接把className设置为"".

Java代码 复制代码
  1. removeClass: function( value ) {      
  2.         if ( jQuery.isFunction(value) ) {   
  3.             return this.each(function(i) {   //这一段同addClass   
  4.                 var self = jQuery(this);   
  5.                 self.removeClass( value.call(this, i, self.attr("class")) );   
  6.             });   
  7.         }   
  8.   
  9.         if ( (value && typeof value === "string") || value === undefined ) {     
  10.     //value为undefined时, remove掉所有的className.   
  11.             var classNames = (value || "").split(rspace);     
  12.     //需要remove掉的classNames, 数组形式   
  13.             for ( var i = 0, l = this.length; i < l; i++ ) {   
  14.                 var elem = this[i];   
  15.                 if ( elem.nodeType === 1 && elem.className ) {   
  16.                     if ( value ) {   
  17.                         var className = (" " + elem.className + " ").replace(rclass, " ");    
  18.      //当前的className中的多个空格合并成一个空格   
  19.                         for ( var c = 0, cl = classNames.length; c < cl; c++ ) {   
  20.                             className = className.replace(" " + classNames[c] + " "" ");     
  21.     //把需要被remove的className替换为""   
  22.                         }   
  23.                         elem.className = jQuery.trim( className );   
  24.                     } else {   
  25.                         elem.className = "";     
  26.     //参数为undefined的时候, 清空className   
  27.                     }   
  28.                 }   
  29.             }   
  30.         }   
  31.         return this;   
  32.     }  
removeClass: function( value ) {   
		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {   //这一段同addClass
				var self = jQuery(this);
				self.removeClass( value.call(this, i, self.attr("class")) );
			});
		}

		if ( (value && typeof value === "string") || value === undefined ) {  
	//value为undefined时, remove掉所有的className.
			var classNames = (value || "").split(rspace);  
	//需要remove掉的classNames, 数组形式
			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];
				if ( elem.nodeType === 1 && elem.className ) {
					if ( value ) {
						var className = (" " + elem.className + " ").replace(rclass, " "); 
	 //当前的className中的多个空格合并成一个空格
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							className = className.replace(" " + classNames[c] + " ", " ");  
	//把需要被remove的className替换为""
						}
						elem.className = jQuery.trim( className );
					} else {
						elem.className = "";  
	//参数为undefined的时候, 清空className
					}
				}
			}
		}
		return this;
	}



jQuery.prorotype.toggleClass

三 切换元素的className
toggleClass就是addClass和removeClass操作的集合.
当这个方法有参数的时候. 如果元素有这个className, 就删除这个className. 反之添加.
如果toggleClass方法没有传入参数. 分别删除之前className和添加之前的className.

另外toggleClass还可以传入一个返回值为boolean的表达式, 当这个值为true时, 执行addClass, 为false时执行removeClass.

和addClass和removeClass一样, toggleClass的第一个参数也可以传入一个闭包.

第一种有参数的情况比较容易想到怎么做. 就是判断当前的className集合, 根据当中有没有跟参数一样的className来看是执行add还是remove操作.

如果没有参数的情况下, 要删除原来所有的className. 这时需要把原来的className设置到元素的缓存当中.以便可以在下次toggleClass的时候恢复.

看代码

Java代码 复制代码
  1. toggleClass: function( value, stateVal ) {   
  2.         var type = typeof value, isBool = typeof stateVal === "boolean";   
  3.   
  4.         if ( jQuery.isFunction( value ) ) {   
  5.             return this.each(function(i) {  //同addClass和removeClass   
  6.                 var self = jQuery(this);   
  7.                 self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );   
  8.             });   
  9.         }   
  10.   
  11.         return this.each(function() {   
  12.             if ( type === "string" ) {   
  13.                 var className, i = 0, self = jQuery(this),   
  14.                     state = stateVal,   
  15.                     classNames = value.split( rspace );   
  16.                 //需要切换的className, 数组形式   
  17.                   while ( (className = classNames[ i++ ]) ) {   
  18.                 //循环数组, 里面的每个className都需要被切换   
  19.                 state = isBool ? state : !self.hasClass( className );   
  20.                 self[ state ? "addClass" : "removeClass" ]( className );   
  21.   
  22. //看元素是执行addClass还是removeClass.    
  23.                    
  24.        }   
  25.   
  26.             } else if ( type === "undefined" || type === "boolean" ) {      
  27.     //删除所有的className或者把原来的所有className重新添加上   
  28.                 if ( this.className ) {   
  29.                     jQuery.data( this"__className__"this.className );   
  30.                 //缓存原来的className   
  31.                 }   
  32.                 this.className = this.className || value === false ? "" : jQuery.data( this"__className__" ) || "";   
  33.                 //切换元素的className为空或者为缓存的className   
  34.             }   
  35.         });   
  36.     }  
toggleClass: function( value, stateVal ) {
		var type = typeof value, isBool = typeof stateVal === "boolean";

		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {  //同addClass和removeClass
				var self = jQuery(this);
				self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
			});
		}

		return this.each(function() {
			if ( type === "string" ) {
				var className, i = 0, self = jQuery(this),
					state = stateVal,
					classNames = value.split( rspace );
				//需要切换的className, 数组形式
	              while ( (className = classNames[ i++ ]) ) {
				//循环数组, 里面的每个className都需要被切换
				state = isBool ? state : !self.hasClass( className );
				self[ state ? "addClass" : "removeClass" ]( className );

//看元素是执行addClass还是removeClass. 
				
       }

			} else if ( type === "undefined" || type === "boolean" ) {   
	//删除所有的className或者把原来的所有className重新添加上
				if ( this.className ) {
					jQuery.data( this, "__className__", this.className );
				//缓存原来的className
				}
				this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
				//切换元素的className为空或者为缓存的className
			}
		});
	}


jQuery.prototype.hasClass

判断是否含有某个特定的className
hasClass是检查当前的元素是否含有某个特定的className, 代码很直观. 不过在判断前也要记得先把className字符串中的多个空格合并成一个.

Java代码 复制代码
  1. hasClass: function( selector ) {   
  2.         var className = " " + selector + " ";   
  3.         for ( var i = 0, l = this.length; i < l; i++ ) {   
  4.             if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {   
  5.                 return true;   
  6.             }   
  7.         }   
  8.         return false;   
  9.     }  
hasClass: function( selector ) {
		var className = " " + selector + " ";
		for ( var i = 0, l = this.length; i < l; i++ ) {
			if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
				return true;
			}
		}
		return false;
	}


jQuery.prototype.val
取得或者设置一个元素的值, 也可以给一组元素, 比如checkbox, radio设置值, 参数是数组形式. 这个方法一般只用于select radio checkbox input textarea等.

这个函数很长, 但代码比较简单. 很多代码都在处理单选select, 多选select, redio, checkbox.

Java代码 复制代码
  1. val: function( value ) {   
  2.         if ( value === undefined ) {     
  3.             //get方式   
  4.             var elem = this[0];     
  5.             //获取第一个元素   
  6.   
  7.             if ( elem ) {   
  8.             //确认元素存在   
  9.                 if ( jQuery.nodeName( elem, "option" ) ){     
  10.             //如果是option   
  11.                     return (elem.attributes.value || {}).specified ? elem.value : elem.text;   
  12.             //如果元素没有value属性, 在IE和firefox下 (elem.attributes.value || {}).specified分别等于false/undefined. 这时返回元素的text.   
  13.             //默认情况下, 如果元素没有value属性. firefox会把元素的text代替value返回. 这里主要是针对IE做的处理.   
  14.                 }   
  15.   
  16.                 if ( jQuery.nodeName( elem, "select" ) ) {   
  17.                     //如果元素是select, 要处理单选和多选
    分享到:
    评论

相关推荐

Global site tag (gtag.js) - Google Analytics