一、$().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;    }

这个代码还挺简单的,就不解析了。


(完)