//对外使用的实例方法
$.fn.extend({
attr
removeAttr
prop
removeProp
addClass
removeClass
toggleClass
hasClass
val
});
//这些工具方法通常是内部使用
$.extend({
valHooks
attr
removeAttr
attrHooks
propFix
prop
propHooks
});
内容解析
attr
和prop
方法的区别(有些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
源码
//[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属性,该方法的属性名不区分大小写
源码
//[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']
源码
//[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
源码
removeProp: function( name ) {
return this.each(function() {
delete this[ jQuery.propFix[ name ] || name ];
});
},
源码
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>
源码
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); //''
源码
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__" ) || "";
}
});
},
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;
},
源码
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']