一、$()
.addClass()
作用:
向目标元素添加一个或多个类名
源码:
//向目标元素添加一个或多个类名
//源码8401行
addClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
i = 0;
//如果addClass(value)的value是一个function
//那么就通过call让目标元素this调用该function
if ( isFunction( value ) ) {
return this.each( function( j ) {
// function(index,currentclass)
// index 对应 j,作用是获取多个目标元素的下标;
// currentClass 对应 getClass(this),作用是获取当前元素的类名,方便加空格
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
} );
}
//将(多个)类名转为数组形式
classes = classesToArray( value );
if ( classes.length ) {
//多个目标元素
while ( ( elem = this[ i++ ] ) ) {
//获取当前值
curValue = getClass( elem );
//如果目标元素是元素节点并且用空格左右包起来 " "+value+" "
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) {
j = 0;
//一个个类名
while ( ( clazz = classes[ j++ ] ) ) {
//当前元素没有和要添加的类名重复的话就添加
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
}
//最后,确认经过处理后的类名集合是否和处理前的类名集合相同
//如果相同,就setAttribute,重新渲染,否则不重新渲染(性能优化)
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
//最后通过setAttribute,添加类名
elem.setAttribute( "class", finalValue );
}
}
}
}
return this;
},
解析:
(1)getClass()
作用:
获取目标元素的类名
源码:
//源码8377行
function getClass( elem ) {
return elem.getAttribute && elem.getAttribute( "class" ) || "";
}
(2)classesToArray
作用:
将(多个)类名转为数组形式
源码:
//源码8382行
function classesToArray( value ) {
//元素的className如果有多个类名的话,是以数组形式保存的,那就直接返回
if ( Array.isArray( value ) ) {
return value;
}
//如果元素类名是string类型的话
if ( typeof value === "string" ) {
return value.match( rnothtmlwhite ) || [];
}
return [];
}
(3)stripAndCollapse
作用:
将vaues以空格分开,再以空格拼接
源码:
// 源码8370行
// Strip and collapse whitespace according to HTML spec
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
function stripAndCollapse( value ) {
var tokens = value.match( rnothtmlwhite ) || [];
return tokens.join( " " );
}
综上:
可以看到 addClass() 的思路是:
① 获取元素当前类名集合 a
② 如果要添加的类名 b 不重复,则将 b 添加进 a 里
③ 最后使用elem.setAttribute("class",a)
完成
二、$()
.removeClass
作用:
移除类,如果没有参数,则移除目标元素所有类名
源码:
//源码8449行
removeClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
i = 0;
//作用同上
if ( isFunction( value ) ) {
return this.each( function( j ) {
jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
} );
}
//如果没有规定参数,则删除所有类
if ( !arguments.length ) {
return this.attr( "class", "" );
}
//将(多个)类名转为数组形式
classes = classesToArray( value );
if ( classes.length ) {
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) {
j = 0;
while ( ( clazz = classes[ j++ ] ) ) {
// 如果当前元素的类名里有要移除的类,
// 就将该类替换成" "
// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
cur = cur.replace( " " + clazz + " ", " " );
}
}
//同上
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue );
}
}
}
}
return this;
},
可以看到 addClass() 的思路是:
① 获取元素当前类名集合 a
② 如果要移除的类名 b 不重复,则将 a 里面的 b 替换成空格 " "
③ 最后使用elem.setAttribute("class",a)
完成移除类名
三、$()
.toggleClass
作用:
切换类
源码:
//stateVal为true,则添加类,false则移除类
//源码8497行
toggleClass: function( value, stateVal ) {
var type = typeof value,
//如果value是string类型或者是数组类型的话,为true,反之为false
isValidValue = type === "string" || Array.isArray( value );
//true添加类,false移除类
if ( typeof stateVal === "boolean" && isValidValue ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}
//同上
if ( isFunction( value ) ) {
return this.each( function( i ) {
jQuery( this ).toggleClass(
value.call( this, i, getClass( this ), stateVal ),
stateVal
);
} );
}
return this.each( function() {
var className, i, self, classNames;
if ( isValidValue ) {
// Toggle individual class names
i = 0;
self = jQuery( this );
classNames = classesToArray( value );
while ( ( className = classNames[ i++ ] ) ) {
//如果目标元素已经有要toggle的className,那么就移除它
// Check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
//否则就添加类
self.addClass( className );
}
}
// Toggle whole class name
}
//如果$.toggleClass()没有值或者该值为布尔值
else if ( value === undefined || type === "boolean" ) {
className = getClass( this );
//如果目标元素有类的话,就先用__className__属性保存类名
if ( className ) {
// Store className if set
dataPriv.set( this, "__className__", 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.
//如果目标元素存在setAttribute的方法话
if ( this.setAttribute ) {
//如果已有类名/value=false,则移除所有类名
//如果没有类名并且value=true,
//则从dataPriv中重新获取之前保存过的__className__当做目标元素的类名
this.setAttribute( "class",
className || value === false ?
"" :
dataPriv.get( this, "__className__" ) || ""
);
}
}
} );
},
解析:
主要是两个 if
(1) if ( typeof stateVal === "boolean" && isValidValue )
这个就是$()
.toggleClass(value,truefalse) 的第二个参数的作用了,
true 即 addClass(),false 即 removeClass()
(2)if(isValidValue )
这个是只有一个参数的情况
利用 hasClass 判断是否 add/removeClass
(3)如果$.toggleClass()没有值或者第一个值为 true 的话
如果目标元素有类名的话,就使用dataPriv
来保存类名,
如果目标元素有setAttribute
的话,则将 className 设置为dataPriv
里保存的值。
四、$()
.hasClass
作用:
检查目标元素是否包含指定的类
源码:
//源码8568行
hasClass: function( selector ) {
var className, elem,
i = 0;
className = " " + selector + " ";
while ( ( elem = this[ i++ ] ) ) {
if ( elem.nodeType === 1 &&
//关键代码
( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
return true;
}
}
return false;
}
这个代码还挺简单的,就不解析了。
(完)