这一课,我们将继续讲解jQuery对元素属性操作的方法。

首先,我们先看一下这几个方法是如何使用的:

$("#div1").addClass("box1 box2");     //给元素div的class属性添加box1和box2

$("#div1").removeClass("box1");     //删除元素div的class属性值box1

$("#div1").toggleClass("box1");     //如果元素div的class属性值中有box1,那么就删除box1。如果没有,那么就添加box1.

$("#div1").hasClass("box1");   //元素div的class属性值是否有box1,如果有,就返回true,如果没有,就返回false。

然后,我们来看一下源码解析:

jQuery.fn.extend({

  ......

  addClass: function( value ) {
    var classes, elem, cur, clazz, j,
      i = 0,
        len = this.length,     //this指的是$("div")
          proceed = typeof value === "string" && value; //判断传入的参数是否是字符串。我们在例子中,传入的都是字符串的形式,其实此方法,还可以传入回调方法,比如:$("div").addClass(function(index){  return "box"+index; })  ,回调方法的返回值,将会作为addClass的参数传入。这段代码就会在第一个div的class属性中添加box0,在第二个div的class属性中添加box1,以此类推。

    if ( jQuery.isFunction( value ) ) {   //传入的参数是否是函数
      return this.each(function( j ) {
        jQuery( this ).addClass( value.call( this, j, this.className ) );  //回调方法的第一个参数是当前元素的index值,第二个参数是当前元素的class属性值。
      });
    }

    if ( proceed ) {   //如果是字符串
      classes = ( value || "" ).match( core_rnotwhite ) || [];  // core_rnotwhite = /\S+/g,把"box1 box2"转换成[box1,box2]

      for ( ; i < len; i++ ) {  //循环元素
        elem = this[ i ];
        cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) :" ");   //如果是元素节点,就继续进行判断元素的class属性值是否存在,rclass = /[\t\r\n\f]/g,\t是制表符,\r是回车,\n换行符,\f是换页。这些都是空白符,不是空格,我们需要把空白符替换成空格,以防元素的class属性值之间用空白符隔开,而不是空格隔开的。比如:<div class="box1  box2">,这里的box1和box2之间的\t就会替换成" "。

        if ( cur ) {   // " ",为真,""为假。
          j = 0;
          while ( (clazz = classes[j++]) ) {
            if ( cur.indexOf( " " + clazz + " " ) < 0 ) {   //判断元素之前是否有此class属性值,没有才添加
              cur += clazz + " ";
            }
          }
          elem.className = jQuery.trim( cur );  //最后,去掉前后空格。

        }
      }
    }

    return this;
  },

  removeClass: function( value ) {
    var classes, elem, cur, clazz, j,
      i = 0,
        len = this.length,
          proceed = arguments.length === 0 || typeof value === "string" && value;  //&&优先级高于||,所以先执行后面的&&操作。当不传入什么参数时,将会删除此元素class所有的属性值。比如:$("#div1").removeClass(),div1元素的class属性值将会变成""。

    if ( jQuery.isFunction( value ) ) {   //如果传入的是回调方法
      return this.each(function( j ) {
        jQuery( this ).removeClass( value.call( this, j, this.className ) );
      });
    }
    if ( proceed ) {   //如果传入的是字符串或者什么都没传
      classes = ( value || "" ).match( core_rnotwhite ) || [];

      for ( ; i < len; i++ ) {
        elem = this[ i ];
        cur = elem.nodeType === 1 && ( elem.className ?( " " + elem.className + " " ).replace( rclass, " " ) :"");

        if ( cur ) {   //如果此元素有class属性值,就进入if语句。
          j = 0;
          while ( (clazz = classes[j++]) ) {
            while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { //如果存在,就把此值删除
              cur = cur.replace( " " + clazz + " ", " " );
            }
          }
          elem.className = value ? jQuery.trim( cur ) : "";  //如果没传入参数,就把元素的class属性值赋为""。
        }
      }
    }

    return this;   //链式操作
  },

  toggleClass: function( value, stateVal ) {   //第二个参数,如果为true,就代表addClass,如果为false,就代表removeClass。
    var type = typeof value;

    if ( typeof stateVal === "boolean" && type === "string" ) {  //$("div").toggleClass("box1 box2",true);
      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" ) {
        var className,
          i = 0,
            self = jQuery( this ),
              classNames = value.match( core_rnotwhite ) || [];

        while ( (className = classNames[ i++ ]) ) {
          if ( self.hasClass( className ) ) {   //元素如果有此class属性值,就删除
            self.removeClass( className );
          } else {
            self.addClass( className );
          }
        }

      } else if ( type === core_strundefined || type === "boolean" ) {  //core_strundefined = undefined,如果是这种操作,$("#div1").toggleClass(false);或者$("#div1").toggleClass();就会进入else if语句。
        if ( this.className ) {   //如果此元素有class属性值,就把属性值存入jQuery缓存系统中。
          data_priv.set( this, "__className__", this.className );
        }

        this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";       //假设div1有class="box1 box2",那么执行$("#div1").toggleClass(false);或者$("#div1").toggleClass();将会把div1的class=""。之后,你再调用$("#div1").toggleClass(true);或者$("#div1").toggleClass();又会把dv1的class="box1 box2"。
      }
    });
  },

  hasClass: function( selector ) {
    var className = " " + selector + " ",
      i = 0,
        l = this.length;  
    for ( ; i < l; i++ ) {    //对所有匹配元素进行class的操作,也就是说$("div"),hasClass("box"),只要页面上的任何一个div的class属性值有box,就会返回true。
      if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
        return true;
      }
    }

    return false;
  },

  ......

});

下一课,将讲解最后一个对属性操作的方法val,因为此方法牵涉到valHooks,因此放到下一课讲解。

加油!

jquery源码解析:addClass,toggleClass,hasClass详解的更多相关文章

  1. jQuery 源码解析(三) pushStack方法 详解

    该函数用于创建一个新的jQuery对象,然后将一个DOM元素集合加入到jQuery栈中,最后返回该jQuery对象,有三个参数,如下: elems Array类型 将要压入 jQuery 栈的数组元素 ...

  2. guava-retrying 源码解析(停止策略详解)

    一.停止策略相关类 1.停止策略接口:StopStrategy接口,只有一个抽象方法 // 是否应该停止重试.不同的停止策略有不同的实现.boolean shouldStop(Attempt fail ...

  3. guava-retrying 源码解析(等待策略详解)

    一.等待策略相关类: 1.等待策略接口:WaitStrategy接口 该接口只有一个方法,就是返回尝试失败之后,下一次尝试之前的等待时间.long computeSleepTime(Attempt f ...

  4. vuex 源码解析(三) getter属性详解

    有时候我们需要从store中的state中派生出一些状态,例如: <div id="app"> <p>{{reverseMessage}}</p> ...

  5. guava-retrying 源码解析(阻塞策略详解)

    这是一种策略,用于决定重试者应如何在重试尝试之间进行阻止.通常这只是一个thread.sleep(),但是如果需要的话,实现可能更复杂. 一.阻塞策略相关的类或接口 1.阻塞策略接口:BlockStr ...

  6. jquery源码解析:代码结构分析

    本系列是针对jquery2.0.3版本进行的讲解.此版本不支持IE8及以下版本. (function(){ (21, 94)     定义了一些变量和函数,   jQuery = function() ...

  7. JQuery源码解析(一)

    写在前面:本<JQuery源码解析>系列是基于一些前辈们的文章进行进一步的分析.细化.修改而写出来的,在这边感谢那些慷慨提供科普文档的技术大拿们. 要查阅JQ的源文件请下载开发版的JQ.j ...

  8. jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究

    终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...

  9. jquery源码解析:jQuery数据缓存机制详解2

    上一课主要讲了jQuery中的缓存机制Data构造方法的源码解析,这一课主要讲jQuery是如何利用Data对象实现有关缓存机制的静态方法和实例方法的.我们接下来,来看这几个静态方法和实例方法的源码解 ...

随机推荐

  1. go 算法 查询字符在字符串中的位置

    在utf8字符串判断是否包含指定字符串,并返回下标."北京天安门最美丽" , "天安门"结果:2 解答: import ( "fmt" &q ...

  2. Navigation and Pathfinding

    [Navigation and Pathfinding] 术语: 1)NavMesh 2)NavMesh Agent 3)Off-Mesh Link 4)NavMesh Obstacle A comm ...

  3. 用jquery实现html5的placeholder功能

    html5的placeholder功能在表单中经常用到,它主要用来提示用户输入信息,当用户点击该输入框之后,提示文字会自动消失. 我们用jquery实现类似的功能: 当输入框获得焦点时,清空输入框中的 ...

  4. APP测试功能点总结

    1.功能性测试:   ——根据产品需求文档编写测试用例. ——软件设计文档编写用例.注意:就是根据产品需求文档编写测试用例而进行测试. 2.兼容性测试:   ——android版本的兼容性 ——手机分 ...

  5. md5,原理待续

    以前项目中copy出来的 import java.security.MessageDigest; public class MD5Util { /** * @todo MD5加码 生成32位md5码 ...

  6. 4-memset函数总结

    头文件:cstring 或 memory 一般用处: memset(arr, 0, sizeof(aar));    //初始化为0 memset(arr, -1, sizeof(aar));   / ...

  7. JSFF或JSF页面加载时触发JavaScript之方法

    现象一 最近在项目中遇到这么一个问题,有些页面元素是在页面加载时通过JavaScript动态渲染而成.当生成这些元素的JavaScript脚本被放置于JSPX文件中时,界面渲染没有问题.但是当我们把生 ...

  8. Python中ndarray数组切片问题a[-n -x:-y]

    先看看如下代码: >>a=np.arange(10)>>a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])>>a[-7:] array( ...

  9. Gym 101201H Paint (离散化+DP)

    题意:给定 n 个区间,让你选出一些,使得每个选出区间不交叉,并且覆盖区间最大. 析:最容易想到的先是离散化,然后最先想到的就是 O(n^2)的复杂度,dp[i] = max(dp[j] + a[i] ...

  10. SpringMVC源码解析- HandlerAdapter - ModelFactory

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...