jQuery源码分析系列:CSS-class样式

jQuery提供了4个操作class的方法:

jQuery.fn.extend({
	// ...
	// 为匹配的每个元素增加指定的class(es)
	addClass: function( value ) {},
	// 从匹配的每个元素上,移除 一个 或 多个 或 全部class
	removeClass: function( value ) {},
	// 对匹配元素集中的每个元素增加或删除一个或多个class
	toggleClass: function( value, stateVal ) {},
	// 检测匹配的元素是否指定了传入的class,只要有一个匹配就返回true
	hasClass: function( selector ) {},
	// ...
});

       

.addClass():
            为匹配的每个元素增加指定的class(es)
            .addClass(className):className添加到每一个匹配元素的class属性上的一个或多个class

            .addClass(function(index,currentClass)):function(index,currentClass)返回一个或多个class名称,多个class用空格分开这些class被添加到现有的class属性中
            index 单签元素在集合中的位置 currentClass 当前的class名     this 指向集合中的当前元素

.addclass()
        addClass:function(value){
            var classNames,i,l,elem,setClass,c,cl;
            //value为函数
            if(jQuery.isFunction(value)){
                return this.each(function(j){
                    //this.className 获取当前的class的值
                    jQuery(this).addClass(value.call(this,j,this.className));
                });
            }
            //vlaue为数组
            if(value && typeof vlaue === "string"){
                classNames = value.split(rspace);//用空白符风格classNames  转换为数组

                for(i=0,l=this.length;i<l;i++){//遍历所有匹配的元素 缓存长度
                    elem = this[i];//缓存下来 避免再次查找
                    if(elem.nodeType === 1){
                        //如果没有class属性 或class属性为空字符串  classNames.length ===1 ,多于一个去重
                        if(!elem.className && classNames.length === 1){
                            elem.className = value;
                        }else{//已有className 或    classNames长度大于1

                            setClass = " " + elem.className + " ";
                            for(c = 0;cl = classNames.length;c<cl:c++){
                                //不理解
                                if(!~setClass.indexOf(" " + classNames[c] + " ")){
                                    setClass += classnames[c] + " ";//追加,最后加一个空格
                                }
                            }
                            //去除首尾空白符
                            elem.className = jQuery.trim(setClass);
                        }
                    }
                }
            }
        },

 


.removeClass():
            从匹配的每个元素上,移除 一个 或 多个 或 全部class

            如果是函数,执行函数,将返回结果再次调用jQuery.fn.removeClass
            如果是字符串,在elem.className和classNames[c]前后加空格,判断replace删除
            如果是undefined,置为空字符串elem.className = ""

            className 一个或多个以空格分隔的class,这些class将被从匹配元素的class属性中移除
            .removeClass( function(index, class) )
            function(index, class) 函数返回一个或多个以空格分隔的class,用于移除。

.removeClass()
        removeClass:function(value){
            var classNames,i,l,elem,className,c,cl;
            //如果传入函数则执行函数 取返回值作为要移除的classNames
            if(jQuery.isFunction(value)){
                return this.each(function(j){
                    //this.calssName    获取当前的class值
                    jQuery(this).removeClass(value.call(this,j,this.className));
                });
            }
            if((value && typeof value === "string") || value === undefined){
                // value || "" 避免空引用错误的常用技巧
                classNames = (value || "").split(rspace);//分割数组

                for(i=0,l=this.length;i<l;i++){//遍历所有匹配的元素 缓存长度
                    elem = this[i];//缓存下来 避免再次查找
                    if(elem.nodeType === 1 && elem.className){
                        //如果有value,则从当前className属性中删除
                        if(value){
                            className = (" " + elem.className + " ").replace(rclass," ");//前后加空格 将\n \t \r替换为空格
                            for(c = 0;cl = classNames.length;c<cl:c++){
                                className = className.replace(" " + classNames[c] + " "," ");//将要删除的className替换为空格
                            }
                            // 删除前后的空白符,然后赋值给elem.className
                            elem.classname = jQuery.trim(className);
                    }else{
                        elem.className = "";
                    }
                }
            }
            return this;
        },

 


.toggleClass():
  对匹配元素集中的每个元素增加或删除一个或多个class
  增加或删除的行为依赖当前元素是否含有指定的class,或switch参数的值

  .toggleClass( className ) 1.0
    className 一个或多个class(用空格隔开),在匹配元素集的每个元素上切换class
     如果集合中的某个元素含有指定的className,className会被删除;如果没有会添加。

  .toggleClass( className, switch ) 1.3
    switch 一个布尔值,依据这个布尔值来决定是添加(true)还是删除(false)

  .toggleClass( [switch] ) 1.4
    switch 一个布尔值,依据这个布尔值来决定是添加还是删除

  .toggleClass( function(index, class, switch) [, switch] ) 1.4
    function(index, class, switch) 函数返回用于切换的calss名称
     index是当前元素是集合中的下标位置, class是当前元素的就class值

  核心技巧:调用addClass 或 removeClass 或 直接赋值elem.className

.toggleClass()
        toggleClass: function( value, stateVal ) {
            var type = typeof value, // value的类型,可以是字符串(一个或多个class),也可以是function,(undefined和boolean是另说)
                isBool = typeof stateVal === "boolean";

             // 如果是函数,则执行函数,用函数的返回值作为切换的className,迭代调用jQuery.fn.toggleClass
            if ( jQuery.isFunction( value ) ) {
                return this.each(function( i ) {
                    // 迭代调用
                    jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
                });
            }

            // 遍历当前jQuery对象
            return this.each(function() {
                // value是字符串,挨个遍历value中的类样式,switch的优先级高于hasClass,hasClass返回false则addClass返回true则removeClass
                if ( type === "string" ) {
                    // toggle individual class names
                    // 切换单个class
                    var className,
                        i = 0,
                        self = jQuery( this ),
                        state = stateVal,
                        classNames = value.split( rspace ); // 可能有多个class,用空白符分割
                     // 因为不需要在className前后加空格,所以这里可以将取值、自增、判断合并为while循环。很好的技巧。
                    while ( (className = classNames[ i++ ]) ) {
                        // check each className given, space seperated list
                        /*
                         * 如果state是布尔值,则以state为准,否则检查是否含有className
                         * 含有 state为false,表示需要addClass;反之需要removeClass
                         * 这个三元表达式合并了state与self.hasClass的判断,小技巧
                         */
                        state = isBool ? state : !self.hasClass( className );
                        //删除还是添加
                        self[ state ? "addClass" : "removeClass" ]( className );
                    }
                /*
                 * type === "undefined"    未指定参数,即.toggleClass()
                 * type === "boolean"     省略className,只有switch,即.toggleClass( switch )
                 */
                // 未指定参数 或 只有switch,则切换整个className
                } else if ( type === "undefined" || type === "boolean" ) {
                     // 如果有className,则缓存下来,以便再次调用时恢复
                    if ( this.className ) {
                        // 以内部数据的方式缓存
                        jQuery._data( this, "__className__", this.className );
                    }

                    /*
                     * 切换整个className
                     * 又是一个合并了几个判断条件的三元,分解为四个逻辑:
                     * this.className && value 是 true/undefined     ""
                     * this.className && value 是 false             ""
                     * !this.className && value 是 true/undefined    jQuery._data( this, "__className__" ) || ""
                     * !this.className && value 是 false             ""
                     *
                     * 分析一下上边的四个逻辑,可以总结如下:(value用switch代替)
                     * 1. 如果this.className存在,无论switch什么状态(true/false/undefined),都置为空""
                     * 2. 如果this.className不存在,如果switch为true/undefined,才会恢复className
                     * 3. 如果this.className不存在,如果switch为false,置空(保持不变)
                     *
                     * .toggleClass( switch )的用法可以总结如下:
                     * 1. switch为true,进行正常的切换,等价于.toggleClass()
                     * 2. switch为false,总是置空
                     */
                    this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
                }
            });
        },

 


.hasClass():
            检测匹配的元素是否指定了传入的class,只要有一个匹配就返回true
            .hasClass() 检测匹配的元素是否指定了传入的class,只要有一个匹配就返回true;元素可能有多个class,在HTML中多个class用空格隔开;
            如果遇到某个元素含有指定的className,.hasClass()将会返回true,即便还指定了其他的className。

.hasClass()
        hasClass:function(selector){
            var className = " " + selector + " ",//前后加空格
                i =0,
                l = this.length;
            for(;i<l:i++){
                 // 必须是Element,技巧同样是前后加空格,同样是indexOf
                if(this[i].nodeType === 1 && ( " " + this[i].className + " ").replcae(rclass," ").indexOf(className) > -1){
                    return true;
                }
            }
            return false;
        }

posted on 2013-04-11 17:21  color_story  阅读(177)  评论(0编辑  收藏  举报

导航