JQ的属性操作方法( 妙味讲堂 - 视频笔记)
1、attr、prop使用对比
1、对比
attr:
通过setAttribute和getAttribute进行设置和获取属性值
prop:
通过obj.prop/obj[prop]来设置和获取属性值,所以自定义属性值是没法操作的
2、实例
设置自带属性都attr、prop都可以实现
设置和获取自定义属性只有attr,prop是无法设置和获取自定义属性的
<div id = 'box'></div>
$("#box").attr("title","hello"); // 设置title属性为hello
$("#box").attr("title"); // 获取title属性值
$("#box").prop("title","hello"); // 设置title属性为hello
$("#box").prop("title"); // 获取title属性值
let box = document.getElementById("box");
box.setAttribute("title","hello");// 设置title属性为hello
box.title = 'hello';// 设置title属性为hello
box['title'] = 'hello';// 设置title属性为hello
box.getAttribute("title"); // 获取title属性值
box.title; // 获取title属性值box['title']; // 获取title属性值2、addClass、removeClass、toggleClass使用与对比
//1、添加还没有的class,已有的就不用添加
<div id = 'box' class = 'box1'></div>
$("#box").addClass("box2 box3");
<div id = 'box1 box2 box3'></div>//2、可以传入函数
<div id = 'box' class = 'box1'></div>
$("#box").addClass(function(index){
console.log(index);//0只有一个元素
return 'box'+index; // 给多个元素添加有规律的class
});
<div id = 'box1 box0'></div>// 3、链式调用
<div id = 'box' class = 'box1'></div>
$("box").addClass("box2").addClass("box3");
<div id = 'box' class = 'box1 box2 box3'></div>
// $("box").addClass("box1")执行完会返回一个对象,所以可以执行下一个操作// 4、移除有的class
<div id = 'box' class = 'box1 box2 box3 box4'></div>
$("#box").removeClass("box3 box4");
<div id = 'box1 box2'></div>// 5、可以传入函数
<div id = 'box' class = 'box1 box2 box3'></div>
$("#box").removeClass(function(index){
console.log(index);//0只有一个元素
return 'box'+(index+1); // 给多个元素添加有规律的class
});
<div id = 'box2 box3'></div>//6、toggleClass 有就删掉,没有就添加
<div id = 'box1 box2'></div>
$("#box").toggleClass("box3 box4");
<div id = 'box1 box2 box3 box4'></div>//7、toggleClass 可以传入参数,对元素数组进行批处理
<div id = 'box' class = 'box1 box2 box3'></div>
$("#box").toggleClass(function(index){
console.log(index);//0只有一个元素
return 'box'+(index+1);// 给多个元素切换有规律的class
});
<div id = 'box2 box3'></div>3、源码实现
1、实例方法
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
},
removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name );
});
},
prop: function( name, value ) {
return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
},
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
});
},
addClass: function( value ) {
var classes, elem, cur, clazz, j,
i = 0, // this 元素对象,多个标签可以同时处理
len = this.length,
proceed = typeof value === "string" && value; // 当传入参数是回调函数的时候
if ( jQuery.isFunction( value ) ) {
// 给元素数组分别添加return设置的class
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)
// 讲传入的参数进行以空白字符为分割点进行分割
// core_rnotwhite = /\S+/g, 匹配所有空格
classes = ( value || "" ).match( core_rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
cur = elem.nodeType === 1 && ( elem.className ?
// rclass = /[\t\r\n\f]/g 空白字符,不止是空格
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
// cur 经过处理没有空白字符
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
// 不存在要添加的class
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
} // trim去掉字符串两边的空白字符
elem.className = jQuery.trim( cur );
}
}
} // 链式调用
// $("box").addClass("box1").addClass("box2");
// $("box").addClass("box1")执行完会返回一个对象,所以可以执行下一个操作
// return this就是返回这么个对象,为了实现链式调用
return this;
}, 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 + " ", " " );
}
}
elem.className = value ? jQuery.trim( cur ) : "";
}
}
}
return this;
}, toggleClass: function( value, stateVal ) {
var type = typeof value;
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,
self = jQuery( this ),
classNames = value.match( core_rnotwhite ) || [];
while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
}
} // Toggle whole class name
} else if ( type === core_strundefined || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery._data( 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 ? "" : jQuery._data( 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 ret, hooks, isFunction,
elem = this[0];
if ( !arguments.length ) {
if ( elem ) {
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret; }
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;
}
});
}});2、工具方法
jQuery.extend({
valHooks: {
option: {
get: function( elem ) {
// Use proper attribute retrieval(#6932, #12072)
var val = jQuery.find.attr( elem, "value" );
return val != null ?
val :
elem.text;
}
},
select: {
get: function( elem ) {
var value, option,
options = elem.options,
index = elem.selectedIndex,
one = elem.type === "select-one" || index < 0,
values = one ? null : [],
max = one ? index + 1 : options.length,
i = index < 0 ?
max :
one ? index : 0;
// Loop through all the selected options
for ( ; i < max; i++ ) {
option = options[ i ];
// oldIE 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;
}
}
},
attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === core_strundefined ) {
return jQuery.prop( elem, name, value );
}
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] ||
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
},
removeAttr: function( elem, value ) {
var name, propName,
i = 0,
attrNames = value && value.match( core_rnotwhite );
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
if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
elem[ propName ] = false;
// Support: IE<9
// Also clear defaultChecked/defaultSelected (if appropriate)
} else {
elem[ jQuery.camelCase( "default-" + name ) ] =
elem[ propName ] = false;
}
// See #9699 for explanation of this approach (setting first, then removal)
} else {
jQuery.attr( elem, name, "" );
}
elem.removeAttribute( getSetAttribute ? name : propName );
}
}
},
attrHooks: {
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;
}
}
}
},
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 );
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 :
( elem[ name ] = value );
} else {
return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
ret :
elem[ name ];
}
},
propHooks: {
// 光标切换,IE某些不是表单组件也可能获取光标,判断是否是可以获取光标元素
tabIndex: {
get: function( elem ) {
var tabindex = jQuery.find.attr( elem, "tabindex" );
// rfocusable = /^(?:input|select|textarea|button|object)$/i,
// 判断标签是否是表单元素
return tabindex ?
parseInt( tabindex, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
-1;
}
}
}});