jQuery源码解析之addClass(),removeClass(),toggleClass()和hasClass()

栏目: jQuery · 发布时间: 6年前

内容简介:一、作用:

jQuery源码解析之addClass(),removeClass(),toggleClass()和hasClass()

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

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

jQuery源码解析之addClass(),removeClass(),toggleClass()和hasClass()

(完)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML Dog

HTML Dog

Patrick Griffiths / New Riders Press / 2006-11-22 / USD 49.99

For readers who want to design Web pages that load quickly, are easy to update, accessible to all, work on all browsers and can be quickly adapted to different media, this comprehensive guide represen......一起来看看 《HTML Dog》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试