测试代码1:获取元素的class数组
var cs="name sex"
alert({}.toString.call(cs.match(/\S+/g)));
//返回[object Array],length是2.默认是空数组[],数组元素默认分隔符是逗号!
测试代码2:正则表达式中的空白符
<p id="p" class="params qinliang">This is a paragraph.</p>
alert(document.getElementById("p").className);//输出"params qinliang"
var rclass = /[\t\r\n\f]/g; //\t为制表符,\r回车符,\n换行符,\f换页符
测试代码3:空格在if语句中会转化为true
//空格会变成true,所以执行下面的if语句
if(" ")
{
alert("空格变成true");//打印true
}else
{
alert("空格变成false");
}
问题1:addClass的源码是什么?
addClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
i = 0,
len = this.length,
proceed = typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
if ( proceed ) {//如果传入的参数是String,我们把它切分为数组!
// The disjunction here is for better compressibility (see removeClass)
classes = ( value || "" ).match( rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
}
// only assign if different to avoid unneeded rendering.
finalValue = jQuery.trim( cur );
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
}
}
}
return this;
}
我们来根据上面的代码举例看看不同情况是怎么调用的
情况1:如果我们传入了函数,那么上下文是调用对象的DOM,第一个参数是该DOM在调用对象的下标,第二个参数表示该元素的className属性
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
//如果addClass传入函数,那么我们把函数执行的结果作为class的值添加到元素的class集合中
//在回调函数中上下文是调用对象的DOM元素,第一个参数是该DOM下标(each第一个参数是下标)
//第二个参数该元素的className
jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
我们通过这种方式来改变body元素的背景色
$("body").addClass(function(index,classN)
{
return "qinliang"+index;
//回调函数中第一个参数是该DON下标,第二个参数是该DOM的className值!
});
情况2:我们把元素本来的className前后各添加一个空格,同时把回车符,换行符等全部替换掉
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
这样做的目地是为了后面判断添加的class是否已经在原来元素的className中,如果在那么我们不会添加,可以减少回流!
情况3:判断添加的class是否已经在元素的className中,如果在不添加,添加的时候后面还要添加一个空格!
while ( (clazz = classes[j++]) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
情况4:如果都添加以后className还是和以前一样,那么我们不修改className,防止没有改变的时候有页面回流和重绘!
finalValue = jQuery.trim( cur );
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
问题2:removeClass的源码是什么?
removeClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
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( 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 + " ", " " );
}
}
// only assign if different to avoid unneeded rendering.
finalValue = value ? jQuery.trim( cur ) : "";
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
}
}
}
return this;
}
removeClass和addClass等都是可以同时添加或者删除多个class值,这时候把值之间用
空格隔开就可以了!
情况1:传入的class是函数,我们把函数调用的结果作为class移除
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).removeClass( value.call( this, j, this.className ) );
});
}
情况2:在开始移除之前,我们把原来的className前后添加空格,同时把回车等全部用空格替换,以便后面用className来进行class的查找
// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
""
);
情况3:用indexOf判断要删除的class是否在元素原来的class中,如果在,我们就删除他,也就是替换为空格!
while ( (clazz = classes[j++]) ) {
// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
cur = cur.replace( " " + clazz + " ", " " );
}
}
情况4:只有前后的class有变化的时候才会修改className值,这样可以防止多余的重绘和回流!
finalValue = value ? jQuery.trim( cur ) : "";
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
问题3: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;
}
通过源码我们知道,hasClass如果判断多于一个的className的值时候,只有参数顺序和原来的className中多个
参数顺序完全一致才可以;同时该方法表明,
只要有一个调用对象的DOM元素包含了这个参数class就会返回true!
问题4:toggleClass源码如何?
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( 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 === 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__" ) || "";
}
});
}
情况1:第一个参数是string,第二个参数是boolean,
这时候如果boolean是true那么添加指定的类型,如果是false表示移除指定的类名
var type = typeof value;
//如果第一个参数是string,第二个参数是boolean,如果boolean是true那么添加,否则移除!
if ( typeof stateVal === "boolean" && type === "string" ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}
情况2:如果第一个参数是函数,那么我们直接调用该函数,把调用的返回值作为第一个参数继续调用toggleClass
if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) {
jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
});
}
此时上下文是DOM,第一个参数是该DOM下标,第二个参数是该元素的className,第三个参数是我们传入的第二个参数!
情况3:如果调用toggleClass时候传入第一个参数是string,那么表示我们希望对这些class进行toggle
if ( type === "string" ) {
// toggle individual class names
var className,
i = 0,
self = jQuery( this ),
//toggleClass如果传入多个calss,其中用空格分开,得到一个需要toggle的class列表!
classNames = value.match( 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
}
这时候如果元素有这些class,那么我们移除这些class,如果没有那么添加!
情况4:如果没有传入参数或者传入的参数只有一个,同时是boolean,那么我们要做特殊处理,修改整个class的值
如果有className,那么我们把className的值保存到内部数据中,同时把className设置为空字符串
如果没有className,同时value不是false,那么我们读取刚才保存到该元素的内部数据className!
if ( type === 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__" ) || "";
}
测试用例1:我们连续调用toggleClass()来进行切换
$('body').toggleClass();
//这时候就会把body元素的class全部保存到body的内部数据中,同时把class设置为空字符串!
var expando=jQuery.expando;
var key=$('body')[0][expando];
var walhouse=jQuery.cache;
var data=walhouse[key];
//这时候是jQuery._data,所以是内部数据,不会保存在data域下面
//打印Object {__className__: "body qinliang0"}
console.log(data);
//这时候继续调用toggleClass不传入参数,就会把刚才保存在内部的数据读取出来作为className值
$('body').toggleClass();
测试用例2:我们连续调用toggleClass()时候,如果第二次调用的时候传入了false,那么表示是
移除而不是添加,所以刚才保存在内部数据中的class不会被重读!
$('body').toggleClass();
//这时候就会把body元素的class全部保存到body的内部数据中,同时把class设置为空字符串!
var expando=jQuery.expando;
var key=$('body')[0][expando];
var walhouse=jQuery.cache;
var data=walhouse[key];
//这时候是jQuery._data,所以是内部数据,不会保存在data域下面
//打印Object {__className__: "body qinliang0"}
console.log(data);
//这时候继续调用toggleClass不传入参数,就会把刚才保存在内部的数据读取出来作为className值
$('body').toggleClass(false);
如果上面第二个参数是true,那么还是可以进行切换的。只要不是明确指定了为false。这就是上面的if判断的情况if ( type === strundefined || type === "boolean" )!