Skip to content

Latest commit

 

History

History
804 lines (629 loc) · 21.7 KB

元素属性.md

File metadata and controls

804 lines (629 loc) · 21.7 KB

12.元素属性

//对外使用的实例方法
$.fn.extend({
	attr
	removeAttr
	prop
	removeProp
	addClass
	removeClass
	toggleClass
	hasClass
	val
});

//这些工具方法通常是内部使用
$.extend({
	valHooks
	attr
	removeAttr
	attrHooks
	propFix
	prop
	propHooks
});

内容解析

attrprop方法的区别(有些HTML属性其实也是element对象的属性,但是element对象的属性并不一定是HTML的属性,所以容易产生混淆)

  • attr方法是设置HTML属性
  • prop方法是设置element对象的属性
//设置元素的默认属性
var $div = $("#a");
$div.attr("href","http://baidu.com");
console.log($div.attr("href"));		//http://baidu.com
$div.prop("href","http://ziyi2.com");
console.log($div.prop("href"));		//http://ziyi2.com/

//设置元素的自定义属性
$div.attr("baidu","http://baidu.com");
console.log($div.attr("baidu"));	//http://baidu.com
$div.prop("ziyi2","http://ziyi2.com");
console.log($div.attr("ziyi2"));	//undefined

12.1 attr()

源码

//[3805] $().attr()
attr: function( name, value ) {
    //最后一个参数用于判断是获取还是设置操作
    //access方法用于在这里用于遍历this这个elems,并且调用jQuery.attr函数进行操作
    return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
},


//[4091] $.attr()
attr: function( elem, name, value ) {
	var hooks, ret,
		nType = elem.nodeType;

	// don't get/set attributes on text, comment and attribute nodes
	// 首先判断elem是不是非文本、注释和属性节点,这些节点不能进行对象属性设置
	if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
		return;
	}

	// Fallback to prop when attributes are not supported
	// 如果getAttribute方法不存在,例如console.log(document.getAttribute)  //undefined
	if ( typeof elem.getAttribute === core_strundefined ) {
		// 那就调用prop方法设置属性,prop方法本质上设置对象的属性操作,使用.或[]方法
		return jQuery.prop( elem, name, value );
	}

	// All attributes are lowercase
	// Grab necessary hook if one is defined
	// 如果不是xml文档,或者不是element对象
	if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
		name = name.toLowerCase();
		//如果设置的是type属性,则走jQuery.attrHooks[ name ]
		//否则匹配这些属性[/^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i]
		//走boolHook函数,因为这些属性都应该可以通过布尔值进行设置
		//如果是其他的属性(例如自定义属性),那就走nodeHook其实是undefined
		hooks = jQuery.attrHooks[ name ] ||
			( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
	}

	//如果value存在
	if ( value !== undefined ) {
		//如果value设置为null则是移除属性
		if ( value === null ) {
			jQuery.removeAttr( elem, name );
		//否则判断hooks是否存在,且hooks.set方法存在(则需要做兼容性处理,使$().attr("checked",true)这样的方法也可以使用)
		} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
			return ret;

		} else {
			//可能value不是字符串,则转化为字符串
			//如果是普通自定义元素,就用原生方法设置
			elem.setAttribute( name, value + "" );
			return value;
		}

	//这个好像一般都不会满足
	} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
		return ret;

	} else {
		//Sizzle里的方法
		ret = jQuery.find.attr( elem, name );

		// Non-existent attributes return null, we normalize to undefined
		return ret == null ?
			undefined :
			ret;
	}
},


//[4159]
attrHooks: {
    type: {
	//这里是对设置radio元素的type属性做兼容性处理
        set: function( elem, value ) {
            if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
                // Setting the type on a radio button after the value resets the value in IE6-9
                // Reset value to default in case type is set after value during creation
                var val = elem.value;
                elem.setAttribute( "type", value );
                if ( val ) {
                    elem.value = val;
                }
                return value;
            }
        }
    }
},

// Hooks for boolean attributes
// 使$().attr("checked",true)这样的方法也可以使用
// 即第二个参数是boolean值也可以正确处理
boolHook = {
	set: function( elem, value, name ) {
		if ( value === false ) {
			// Remove boolean attributes when set to false
			// 参数为false时移除属性
			jQuery.removeAttr( elem, name );
		} else {
			//参数为true时设置属性的值为属性
			//详见(二)
			elem.setAttribute( name, name );
		}
		return name;
	}
};

内容解析

(一)设置布尔值属性时可以使用布尔值参数

//设置元素的默认属性
var $input = $("#radio");
$input.attr("checked","checked");
$input.attr("checked",true);	//
$input.attr("checked",false);	//可以

//原生方法设置
var input = document.getElementById("radio");
input.setAttribute("checked",true);
input.setAttribute("checked",false);	//这样是不能取消被选中的状态


//设置元素的默认属性
var $input = $("#radio");
$input.attr("checked",true);
console.log($input.attr("checked"));	//checked
$input.attr("type","radio");		//其实是做了兼容性处理,这里需要先设置type属性,然后获取元素的value值并重新设置value的值

(二)element.setAttribute

  • 可以获取和设置非标准的HTML属性,该方法的属性名不区分大小写

12.2 removeAttr()

源码

//[3809] $().removeAttr()
removeAttr: function( name ) {
    return this.each(function() {
        jQuery.removeAttr( this, name );
    });
},

//[4139] $.removeAttr()
removeAttr: function( elem, value ) {
    var name, propName,
        i = 0,
	//详见(二)
        attrNames = value && value.match( core_rnotwhite );

    //第一个参数必须是element对象
    if ( attrNames && elem.nodeType === 1 ) {
        while ( (name = attrNames[i++]) ) {
            propName = jQuery.propFix[ name ] || name;

            // Boolean attributes get special treatment (#10870)
            if ( jQuery.expr.match.bool.test( name ) ) {
                // Set corresponding property to false
		// 布尔值的属性需要设置为false
                elem[ propName ] = false;
            }

	    //原生方法删除属性
            elem.removeAttribute( name );
        }
    }
},

//兼容性,for和class本身是关键字
propFix: {
    "for": "htmlFor",
    "class": "className"
},

内容解析

(一) 删除多个属性

var $input = $("#radio");
$input.removeAttr("id class checked");	//删除多个属性

$input[0].checked = true;
$input[0].removeAttribute("checked");	//element的属性checked仍然为false

(二) 匹配多个空格

var pattern = /\S+/g
    , str = "a b c d e f"
    , strArr = str.match(pattern);

console.log(strArr); //['a','b','c','d','e','f']

12.3 prop()

源码

//[3815] $().prop()
prop: function( name, value ) {
    return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
},



// $.
propFix: {
    "for": "htmlFor",
    "class": "className"
},

prop: function( elem, name, value ) {
    var ret, hooks, notxml,
        nType = elem.nodeType;

    // don't get/set properties on text, comment and attribute nodes
    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
        return;
    }

    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

    // 如果不是xml,则可能有兼容性问题需要处理
    if ( notxml ) {
        // Fix name and attach hooks
        name = jQuery.propFix[ name ] || name;
        hooks = jQuery.propHooks[ name ];
    }

    // 设置值
    if ( value !== undefined ) {
        return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
            ret :
            //设置的其实是element对象属性
            ( elem[ name ] = value );

    // 获取值
    } else {
        return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
            ret :
            elem[ name ];
    }
},


propHooks: {
    //tabIndex具有兼容性问题
    tabIndex: {
        get: function( elem ) {
            return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
                elem.tabIndex :
                -1;
        }
    }
}

内容解析

var input = $("#radio");
input.prop('ziyi2',"ziyi2");
console.log(input.prop('ziyi2'));	//ziyi2

12.4 removeProp()

源码

removeProp: function( name ) {
    return this.each(function() {
        delete this[ jQuery.propFix[ name ] || name ];
    });
},

12.5 addClass()

源码

addClass: function( value ) {
    var classes, elem, cur, clazz, j,
        i = 0,
        len = this.length,
        proceed = typeof value === "string" && value;

    // 如果参数是函数,则函数的参数是this的index和对应的className
    if ( jQuery.isFunction( value ) ) {
        return this.each(function( j ) {
            jQuery( this ).addClass( value.call( this, j, this.className ) );
        });
    }

    if ( proceed ) {
        // The disjunction here is for better compressibility (see removeClass)
	// value转换成数组
        classes = ( value || "" ).match( core_rnotwhite ) || [];

        for ( ; i < len; i++ ) {
            elem = this[ i ];
	    // rclass = /[\t\r\n\f]/g, 制表符 换行符 回车符等
	    // 将html中元素的class的值的制表符等转换为空字符
            cur = elem.nodeType === 1 && ( elem.className ?
                ( " " + elem.className + " " ).replace( rclass, " " ) :
                " "
            );

            if ( cur ) {
                j = 0;
                while ( (clazz = classes[j++]) ) {
		    // 如果html的class中没有需要设置的class
                    if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
                        cur += clazz + " ";
                    }
                }
		//去掉之前加上的两边的空格
                elem.className = jQuery.trim( cur );

            }
        }
    }

    return this;
},

内容解析

<div id="div" class="box
	box1		box3"></div>	<!-- 有换行和Tab键-->
<script src="Jquery2.0.3.js"></script>
<script>
    var $div = $("#div");
    div.addClass("box		box1 box2 box4" );
    $div.addClass(function(index, className) {	//适合多个元素时利用index设置class
	//indexOf如果找到了则返回找到的位置
	if(className.indexOf('box4') > -1) {
		return "box5";
	}
    });
</script>

12.6 removeClass()

源码

removeClass: function( value ) {
    var classes, elem, cur, clazz, j,
        i = 0,
        len = this.length,
	//详见(一)
        proceed = arguments.length === 0 || typeof value === "string" && value;

    if ( jQuery.isFunction( value ) ) {
        return this.each(function( j ) {
            jQuery( this ).removeClass( value.call( this, j, this.className ) );
        });
    }
    if ( proceed ) {
        classes = ( value || "" ).match( core_rnotwhite ) || [];

        for ( ; i < len; i++ ) {
            elem = this[ i ];
            // This expression is here for better compressibility (see addClass)
            cur = elem.nodeType === 1 && ( elem.className ?
                ( " " + elem.className + " " ).replace( rclass, " " ) :
                ""
            );

            if ( cur ) {
                j = 0;
                while ( (clazz = classes[j++]) ) {
                    // Remove *all* instances
                    while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
                        cur = cur.replace( " " + clazz + " ", " " );
                    }
                }
		//如果value不存在,则去掉所有的class
                elem.className = value ? jQuery.trim( cur ) : "";
            }
        }
    }

    return this;
},

内容解析

(一) 优先级

console.log(1 || 0 && 2);	//1, 如果是||优先级高,则返回2,否则返回1,说明&&优先级高
$("#div").removeClass();
console.log($("div")[0].className);	//''

12.7 toggleClass()

源码

toggleClass: function( value, stateVal ) {
    var type = typeof value;

    //如果存在第二参数且是布尔值,则功能类似于addClass和removeClass
    if ( typeof stateVal === "boolean" && type === "string" ) {
        return stateVal ? this.addClass( value ) : this.removeClass( value );
    }

    //同样支持回调函数
    if ( jQuery.isFunction( value ) ) {
        return this.each(function( i ) {
            jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
        });
    }

    return this.each(function() {
        if ( type === "string" ) {
            // toggle individual class names
            var className,
                i = 0,
		//需要注意前面是this.each,所以这里的this并不指代$(),而是指示具体的元素
		//jQuery(this)就是获取了实例对象,所以才可以调用实例对象的方法
                self = jQuery( this ),
                classNames = value.match( core_rnotwhite ) || [];

            while ( (className = classNames[ i++ ]) ) {
                // check each className given, space separated list
		//如果有class
                if ( self.hasClass( className ) ) {
                    self.removeClass( className );
                } else {
                    self.addClass( className );
                }
            }

        // Toggle whole class name
	// 如果第一参数不存在或者是布尔值,则是反转所有的className,其实是通过data方法将之前的class全部缓存起来
        } else if ( type === core_strundefined || type === "boolean" ) {
            if ( this.className ) {
                // store className if set
                data_priv.set( this, "__className__", this.className );
            }

            // If the element has a class name or if we're passed "false",
            // then remove the whole classname (if there was one, the above saved it).
            // Otherwise bring back whatever was previously saved (if anything),
            // falling back to the empty string if nothing was stored.
            this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
        }
    });
},

12.8 hasClass()

hasClass: function( selector ) {
    var className = " " + selector + " ",
        i = 0,
        l = this.length;
    for ( ; i < l; i++ ) {
        if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
            return true;
        }
    }

    return false;
},

12.9 val()

源码

val: function( value ) {
    var hooks, ret, isFunction,
        elem = this[0];

    //没有参数就是获取值
    if ( !arguments.length ) {
        if ( elem ) {
	    //对于select元素,elem.type=select-one/select-multiple,因此要使用elem.nodeName.toLowerCase()来获取select属性
            hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];

	    //select/option/checkbox具有get属性
            if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
                return ret;
            }

	    //对于普通的text texterea元素就会直接获取value属性值
            ret = elem.value;

            return typeof ret === "string" ?
                // handle most common string cases
                ret.replace(rreturn, "") :
                // handle cases where value is null/undef or number
                ret == null ? "" : ret;
        }

        return;
    }

    isFunction = jQuery.isFunction( value );

    return this.each(function( i ) {
        var val;

        if ( this.nodeType !== 1 ) {
            return;
        }

        if ( isFunction ) {
            val = value.call( this, i, jQuery( this ).val() );
        } else {
            val = value;
        }

        // Treat null/undefined as ""; convert numbers to string
        if ( val == null ) {
            val = "";
        } else if ( typeof val === "number" ) {
            val += "";
        } else if ( jQuery.isArray( val ) ) {
            val = jQuery.map(val, function ( value ) {
                return value == null ? "" : value + "";
            });
        }

        hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

        // If set returns undefined, fall back to normal setting
        if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
            this.value = val;
        }
    });
}


valHooks: {
 option: {
      get: function( elem ) {
          // attributes.value is undefined in Blackberry 4.7 but
          // uses .value. See #6932
	  // elem.attributes 是元素的所有属性的集合
          // 判断value属性是不是存在
          var val = elem.attributes.value;
	  // val.specified 判断value属性是否指定了值,如果指定了值则返回指定值,否则返回元素的text文本
          return !val || val.specified ? elem.value : elem.text;
      }
  },
  select: {
      get: function( elem ) {
          var value, option,
	      //获取select元素的所有option
              options = elem.options,
              //获取选中元素的索引值,多选时是所有被选中元素的最小索引值
              index = elem.selectedIndex,
              //判断select的类型是单选还是多选
              one = elem.type === "select-one" || index < 0,
              //单选返回单个值,多选返回数组
              values = one ? null : [],
              //要遍历的最大值,其实单选可以不写,这里是为了让单选和多选做代码统一
              max = one ? index + 1 : options.length,
	      //单选的时候i = index
	      //多选时i = 0
              i = index < 0 ?
                  max :
                  one ? index : 0;

          // Loop through all the selected options
	  // 单选只会遍历一次,从i开始到i+1结束
          for ( ; i < max; i++ ) {
              option = options[ i ];

              // IE6-9 doesn't update selected after form reset (#2551)
              if ( ( option.selected || i === index ) &&
                      // Don't return options that are disabled or in a disabled optgroup
                      ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
                      ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {

                  // Get the specific 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;
      },

      set: function( elem, value ) {
          var optionSet, option,
              options = elem.options,
              values = jQuery.makeArray( value ),
              i = options.length;

          while ( i-- ) {
              option = options[ i ];
              if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
                  optionSet = true;
              }
          }

          // force browsers to behave consistently when non-matching value is set
          if ( !optionSet ) {
              elem.selectedIndex = -1;
          }
          return values;
      }
  }
},


// Radios and checkboxes getter/setter
jQuery.each([ "radio", "checkbox" ], function() {
	jQuery.valHooks[ this ] = {
		set: function( elem, value ) {
			if ( jQuery.isArray( value ) ) {
				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
			}
		}
	};
	if ( !jQuery.support.checkOn ) {
		jQuery.valHooks[ this ].get = function( elem ) {
			// Support: Webkit
			// "" is returned instead of "on" if a value isn't specified
			return elem.getAttribute("value") === null ? "on" : elem.value;
		};
	}
});

内容解析

//1. 普通元素
var $text = $("#text");
$text.val('ziyi2');			//类似于$text[0].value = 'ziyi2';
console.log($text.val());	//ziyi2


//2. option元素
/**
 * <select id="select">
        <option>1</option>
        <option selected value="value_1">2</option>
        <option>value_2</option>
    </select>
 */
var $option = $('option');
//原生写法
console.log($option.eq(0).get(0).value);	//1
console.log($option.eq(1).get(0).value);	//value_1
console.log($option.eq(2).get(0).value);	//value_2

//jquery写法
console.log($option.eq(0).val());			//1
console.log($option.eq(1).val());			//value_1 如果value = "", 则返回""



//3. select单选元素,option当有value属性时获取value属性值,否则获取元素文本内容

/**
 * <select id="select">
        <option>1</option>
        <option value="value_1">2</option>
        <option>value_2</option>
    </select>
 */
console.log($('select').eq(0).get(0).type);	//select-one
console.log($('select').eq(0).val());		//1 默认第一个元素是选中的


//4. select多选元素
/**
 * <select id="select" multiple>
        <option>1</option>
        <option selected>2</option>
        <option selected value="value_3">3</option>
    </select>
 */
var $select = $('select').eq(1);			//select-multiple
console.log($select.get(0).type);
console.log($select.val());					//返回的是数组 ['2','value_3']