jQuery.fn.attr方法:
attr: function( name, value ) { // access(elems, key, value, exec, fn) // elems = this, key = name, value = value, exec = true, fn = jQuery.attr return access(this, name, value, true, jQuery.attr);}, |
调用access方法:
// Mutifunctional method to get and set values to a collection// The value/s can be optionally by executed if its a functionfunction access( elems, key, value, exec, fn ) { var l = elems.length; // Setting many attributes if ( typeof key === "object" ) { for (var k in key) { // 如果key是一个object,递归调用access方法 access(elems, k, key[k], exec, fn); } return elems; } // Setting one attribute if (value !== undefined) { // Optionally, function values get executed if exec is true exec = exec && jQuery.isFunction(value); for (var i = 0; i < l; i++) { var elem = elems[i], val = exec ? value.call(elem, i) : value; // 对于jQuery.fn.attr()调用,这里的fn就是jQuery.attr()方法 fn(elem, key, val); } return elems; } // Getting an attribute return l ? fn(elems[0], key) : null;} |
当获取jquery对象的某个属性值时,会调用fn(elems[0], key),即jQuery.attr()方法:
jQuery.extend({ attr: function( elem, name, value ) { // don't set attributes on text and comment nodes if (!elem || elem.nodeType == 3 || elem.nodeType == 8) return undefined; // name可能是jQuery.fn的一个方法,如.text(), .css(), .click() // 把value作为name方法的参数进行调用,并返回调用结果 // 这个可以结合jQuery()方法使用,如下面例子中的text这个key,a标签并没有text属性,将会调用jQuery.fn.text()方法: // jQuery('<a/>', { id: 'foo', href: 'http://www.google.com', title: 'a Googler', rel: 'external', text: 'Google!' }); if ( name in jQuery.fn && name !== "attr" ) { return jQuery(elem)[name](value); } var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), // Whether we are setting (or getting) set = value !== undefined; // Try to normalize/fix the name name = notxml && jQuery.props[ name ] || name; // Only do all the following if this is a node (faster for style) if ( elem.nodeType === 1 ) { // These attributes require special treatment var special = /href|src|style/.test( name ); // Safari mis-reports the default selected property of a hidden option // Accessing the parent's selectedIndex property fixes it if ( name == "selected" && elem.parentNode ) elem.parentNode.selectedIndex; // If applicable, access the attribute via the DOM 0 way if ( name in elem && notxml && !special ) { if ( set ){ // We can't allow the type property to be changed (since it causes problems in IE) if ( name == "type" && /(button|input)/i.test(elem.nodeName) && elem.parentNode ) throw "type property can't be changed"; elem[ name ] = value; } // browsers index elements by id/name on forms, give priority to attributes. if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) return elem.getAttributeNode( name ).nodeValue; // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ if ( name == "tabIndex" ) { var attributeNode = elem.getAttributeNode( "tabIndex" ); return attributeNode && attributeNode.specified ? attributeNode.value : /(button|input|object|select|textarea)/i.test(elem.nodeName) ? 0 : /^(a|area)$/i.test(elem.nodeName) && elem.href ? 0 : undefined; } return elem[ name ]; } if ( !jQuery.support.style && notxml && name == "style" ) { if ( set ) elem.style.cssText = "" + value; return elem.style.cssText; } if ( set ) // convert the value to a string (all browsers do this but IE) see #1070 elem.setAttribute( name, "" + value ); var attr = !jQuery.support.hrefNormalized && notxml && special // Some attributes require a special call on IE ? elem.getAttribute( name, 2 ) : elem.getAttribute( name ); // Non-existent attributes return null, we normalize to undefined return attr === null ? undefined : attr; } // elem is actually elem.style ... set the style // Using attr for specific style information is now deprecated. Use style insead. return jQuery.style(elem, name, value); }}); |
注意其中二句// These attributes require special treatmentvar special = /href|src|style/.test( name );
当传给attr方法第二个参数时,如果不是一个function,则以第二个参数给elem的属性赋值,调用fn(elem, key, val),即jQuery.attr(elem, key, val)方法。
如果第二个参数是一个function,则将此方法做为当前元素elem的方法调用,参数则为当前elem的index值,用此方法计算返回的值赋给此elem。
// Optionally, function values get executed if exec is trueexec = exec && jQuery.isFunction(value);for (var i = 0; i < l; i++) { var elem = elems[i], val = exec ? value.call(elem, i) : value; fn(elem, key, val);} |
注意jQuery.attr方法中的这段代码,因为IE的原因,所以不能改变一个input元素的type值。如果要更改type属性,可以进行元素替换。jQuery没有在此进行替换操作,而是扔了一个异常出来给调用者。
if ( set ){ // We can't allow the type property to be changed (since it causes problems in IE) if ( name == "type" && /(button|input)/i.test(elem.nodeName) && elem.parentNode ) throw "type property can't be changed"; elem[ name ] = value;} |
最后由access()方法中的return elems返回原来的jquery对象。// Setting one attributeif (value !== undefined) { // Optionally, function values get executed if exec is true exec = exec && jQuery.isFunction(value); for (var i = 0; i < l; i++) { var elem = elems[i], val = exec ? value.call(elem, i) : value; fn(elem, key, val); } return elems;}
当将最后一个class name从元素的class中移除时,其class=" ",为包含一个空格的字符串。
removeClass: function( value ) { if ( (value && typeof value === "string") || value === undefined ) { var classNames = (value || "").split(/\s+/); 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 + " "; for ( var c = 0, cl = classNames.length; c < cl; c++ ) { className = className.replace(" " + classNames[c] + " ", " "); } elem.className = className.substring(1, className.length - 1); } else { elem.className = ""; } } } } return this;}, |
注意其中的replace语句,这个最后使得className被置为一个空格字符串" ",最后返回jquery对象:className = className.replace(" " + classNames[c] + " ", " ");
attr( properties ),同时为某个jquery对象设置多个属性,这里其实是用了递归调用access()方法本身。// Setting many attributesif ( typeof key === "object" ) { for (var k in key) { access(elems, k, key[k], exec, fn); } return elems;}
jquery.toggleClass()方法调用时进入this.each( fn, arguments ),进行toggleClass方法定义,这种做法在jquery源码中很多见,利用jQuery.each()方法将一个匿名对象的方法移到另一个对象的原型链中:
另外注意: state = isBool ? state : !jQuery(this).hasClass( className );
这句,如果传入的status为false,则不管这个jquery对象是否有这个className都会调用removeClass进行移除操作。
jQuery.each({ // 删除DOM元素中名为name的属性 removeAttr: function( name ) { jQuery.attr( this, name, "" ); if (this.nodeType == 1) this.removeAttribute( name ); }, toggleClass: function( classNames, state ) { var type = typeof classNames; if ( type === "string" ) { // toggle individual class names var isBool = typeof state === "boolean", className, i = 0, classNames = classNames.split( /\s+/ ); while ( (className = classNames[ i++ ]) ) { // check each className given, space seperated list state = isBool ? state : !jQuery(this).hasClass( className ); jQuery(this)[ state ? "addClass" : "removeClass" ]( className ); } } else if ( type === "undefined" || type === "boolean" ) { if ( this.className ) { // store className if set jQuery.data( this, "__className__", this.className ); } // toggle whole className this.className = this.className || classNames === false ? "" : jQuery.data( this, "__className__" ) || ""; } }}, function(name, fn){ // 这个function被jQuery.each方法调用执行,方法体本身没有返回结果,只是为了定义二个新方法给jquery原型对象。 // 下面这3行被用于定义新方法。 jQuery.fn[ name ] = function(){ return this.each( fn, arguments ); };}); |
还有一种toggleClass()的用法就是不传参数(其实看源码可以看到还可传个布尔值)时调用,
如果元素有className属性值,则会将此值存到此元属对应的data中,并且将className置空,另外如果传入的是false的话就不管此元素的className是否有值,都会将此className置空。
如果传入的值为true,则会toggle class。
这里需要注意它这句话,因为或运算符||比三目运算符?:的优先级别高,而全等===运算符又比或运算符||优先级高。
也就是说先计算classNames === false,
再计算this.className || classNames === false,
和jQuery.data( this, "className" ) || "",
最后进行三目运算。
// toggle whole classNamethis.className = this.className || classNames === false ? "" : jQuery.data( this, "__className__" ) || "";// 这句话写得看上去简单,实际上理解有点难。 |
jquery.val()方法,不传参数是得到匹配的元素集中的第一个对象的值。如果传了一个String参数的话,会将此值赋给所有的匹配到的元素。
如果传了一个Array的参数,则可以用于多选择框,或者是选中符合数组值的匹配元素。
val: function( value ) { if ( value === undefined ) { var elem = this[0]; if ( elem ) { if( jQuery.nodeName( elem, 'option' ) ) return (elem.attributes.value || {}).specified ? elem.value : elem.text; // We need to handle select boxes special if ( jQuery.nodeName( elem, "select" ) ) { var index = elem.selectedIndex, values = [], options = elem.options, one = elem.type == "select-one"; // Nothing was selected if ( index < 0 ) return null; // Loop through all the selected options // 这段for循环可以把one这块内容移出来,不要放在for里面,直接在外面拿select.options[select.selectedIndex]这个option对象的value // for只是处理一下多项选择就可以更清晰。 for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { var option = options[ i ]; if ( option.selected ) { // Get the specifc value for the option value = jQuery(option).val(); // We don't need an array for one selects if ( one ) return value; // Multi-Selects return an array values.push( value ); } } return values; } // Everything else, we just grab the value return (elem.value || "").replace(/\r/g, ""); } return undefined; } // Typecast once if the value is a number if ( typeof value === "number" ) value += ''; var val = value; return this.each(function(){ // 传进来的value也可以是一个方法,将方法做为此元素的方法来调用(作用域就设置在此元素之上),将调用结果做为新的value。 if(jQuery.isFunction(value)) { val = value.call(this); // Typecast each time if the value is a Function and the appended // value is therefore different each time. if( typeof val === "number" ) val += ''; } // 这句判断应该是在最前面就可以被执行掉。 if ( this.nodeType != 1 ) return; // 传个数组进来用于选中对应的checkbox/radio/select,这个方法并不太好,用处不大并会造成一些误操作。 // 如果传进来的value为一个数组时,当元素的值在此数组中时,则选中radio/checkbox。 if ( jQuery.isArray(val) && /radio|checkbox/.test( this.type ) ) this.checked = jQuery.inArray(this.value || this.name, val) >= 0; else if ( jQuery.nodeName( this, "select" ) ) { var values = jQuery.makeArray(val); jQuery( "option", this ).each(function(){ // 当option的值在数组中时,选中此option。 this.selected = jQuery.inArray( this.value || this.text, values ) >= 0; }); if ( !values.length ) this.selectedIndex = -1; } else // set此对象的值为val,如果value是个function,则这个val是调用结果。 this.value = val; });} |
对于option,如果没有明确指定option value这个属性,则返回text做为值,另外其源码里这个写法可以少掉一个if/else语句,非常清晰明了:
(elem.attributes.value || {}).specified
因为value如果在option中没有指定,则这个elem.attribtues.value为undefined,不会有specified属性,但在空对象获取这个属性则不会报错,只是返回一个undefined而已。return (elem.attributes.value || {}).specified ? elem.value : elem.text;