• jquery源码解析:addClass,toggleClass,hasClass详解


    这一课,我们将继续讲解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 = /[ f]/g, 是制表符, 是回车, 换行符,f是换页。这些都是空白符,不是空格,我们需要把空白符替换成空格,以防元素的class属性值之间用空白符隔开,而不是空格隔开的。比如:<div class="box1  box2">,这里的box1和box2之间的 就会替换成" "。

            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,因此放到下一课讲解。

    加油!

  • 相关阅读:
    全程记录:今天尝试安装SharePoint Server 2007过程 ,安装成功了,但是开始使用碰到权限问题,应该算是BUg吧 无为而为
    IT人看《国富论》系列:第一篇之第九章:论资本利润,暴利或许意味着市场的调节已经失效了,比如房地产 无为而为
    组建InfoPath/SharePoint/WebParts项目组,已收到项目意向,欢迎每周至少有20小时左右的空余时间有项目经验者加入。 无为而为
    我有问题请教各位硬件高手:电脑呈现这个症状是不是电源的输出功率不够? 无为而为
    使用VS2005新功能的Tips:this.Validate();这一句话,让我找了好久。 无为而为
    MSF for CMMI Process Improvement项目体验: Work Items : Requirement (更新版) 无为而为
    等待新产品还是自己开发,这是个两难的问题 无为而为
    在播放器战争爆发之前:比较苹果的iTunes 6和微软Media Player 10 无为而为
    LCS管理员的工具:Live Communications Server 2005 with Service Pack 1 Resource Kit,比如追踪登陆过程 无为而为
    Ms sql 等待一定时间后取数据
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4192529.html
Copyright © 2020-2023  润新知