测试代码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值,这时候把值之间用 空格隔开就可以了!
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!
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,第三个参数是我们传入的第二个参数!
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,如果没有那么添加!
如果没有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" )!