jQ的class属性操作
发布在# 菜鸟解读 jQuery #2014年5月29日view:10139
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

本篇介绍的是关于class属性操作的api:
有 $().addClass(), $().removeClass(), $().toggleClass(), $().hasClass()
逻辑并不饶腾,只需逐行阅读即可,没有跳跃性

jQuery.fn.extend({
    addClass: function( value ) {
        var classNames, i, l, elem,
            setClass, c, cl;
        if ( jQuery.isFunction( value ) ) {
            return this.each(function( j ){
                //传入索引 和 现有的整个class字符串,并将返回值进行进行执行addClass在做
                jQuery( this ).addClass( value.call(this, j, this.className) );
            });
        }
        if ( value && typeof value === "string" ) {
            //分割
            classNames = value.split( rspace );
            for ( i = 0, l = this.length; i < l; i++ ) {
                elem = this[ i ];
                //是否是dom元素
                if ( elem.nodeType === 1 ) {
                    //如果没有class字符串 && 要添加的也是一个单一字符串,则直接进行赋值即可
                    if ( !elem.className && classNames.length === 1 ) {
                        elem.className = value;
                    } else {
                        //将原dom class字符串两端添加空格,这是为了保证每个class字符串两边必然出现空格
                        setClass = " " + elem.className + " ";
                        for ( c = 0, cl = classNames.length; c < cl; c++ ) {
                            //~是 “按位非” 运算符,相当于改变它的符号并且减一
                            //也就是说对于 -1 来说,运算后刚好是0,加!变成了 true
                            if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
                                //追加新的class属性
                                setClass += classNames[ c ] + " ";
                            }
                        }
                        //进行赋值,这里还是用了$.trim将两边空白去掉
                        elem.className = jQuery.trim( setClass );
                    }
                }
            }
        }
        return this;
    },
    removeClass: function( value ) {
        var classNames, i, l, elem, className, c, cl;
        if ( jQuery.isFunction( value ) ) {
            //这里的逻辑和 addClass一样的
            return this.each(function( j ) {
                jQuery( this ).removeClass( value.call(this, j, this.className) );
            });
        }
        //相比addClass,removeClass允许不传入内容,
        //即:删除所有class属性
        if ( (value && typeof value === "string") || value === undefined ) {
            classNames = ( value || "" ).split( rspace );
            for ( i = 0, l = this.length; i < l; i++ ) {
                elem = this[ i ];
                if ( elem.nodeType === 1 && elem.className ) {
                    if ( value ) {
                        // rclass = /[\n\t\r]/g
                        className = (" " + elem.className + " ").replace( rclass, " " );
                        //删除这个操作更简单哈,只要匹配到然后替换成空即可
                        for ( c = 0, cl = classNames.length; c < cl; c++ ){
                            className = className.replace(" " + classNames[ c ] + " ", " ");
                        }
                        //将删除后的元素进行设置
                        elem.className = jQuery.trim( className );
                    } else {
                        //直接删除所有className
                        elem.className = "";
                    }
                }
            }
        }
        return this;
    },
    //交替class属性,有则取消,无则添加
    toggleClass: function( value, stateVal ) {
        var type = typeof value,
            isBool = typeof stateVal === "boolean";
        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" ) {
                // toggle individual class names
                var className,
                    i = 0,
                    self = jQuery( this ),
                    state = stateVal,
                    classNames = value.split( rspace );
                while ( (className = classNames[ i++ ]) ) {
                    //如果第二个参数是boolean值: 
                    //true,则相当于addClass;false,则相当于removeClass
                    //不传入则进行替换逻辑
                    state = isBool ? state : !self.hasClass( className );
                    //jQ的写法好简洁诶..
                    self[ state ? "addClass" : "removeClass" ]( className );
                }
            //如果只传入了第二个参数 || 或者value是false
            //则对整个class字符串执行设置和取消
            } else if ( type === "undefined" || type === "boolean" ) {
                if ( this.className ) {
                    // 保存当前className信息
                    jQuery._data( this, "__className__", this.className );
                }
                // 如果有值 || 传入的false ? "" : .__className__
                this.className = this.className || value === false ? 
                "" : 
                jQuery._data( this, "__className__" ) || "";
            }
        });
    },
    //这个逻辑不复杂,就是查找指定的 selector 在元素的class属性上存不存在
    //有一项存在就返回true,否则就返回false
    hasClass: function( selector ) {
        var className = " " + selector + " ",
            i = 0,
            l = this.length;
        for ( ; i < l; i++ ) {
            if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
                return true;
            }
        }
        return false;
    }
});

本篇就到这里,感谢您的阅读
欢迎留言:提问,交流,斧正

评论
发表评论
暂无评论
WRITTEN BY
前端狮子
JS前端开发工程师 :喜欢研究js,nodejs,html5; 希望结交更多朋友~
TA的新浪微博
PUBLISHED IN
# 菜鸟解读 jQuery #

本栏解读的jQ为1.7.2版本。 本人也是刚开始读起源码,在这里分享下成长的心得。 本人能力有限,也是接触JS不久的初学者,定会有不少解析不全不够明朗【甚至BUG】的地方, 希望各位牛牛多多留言斧正 感谢阅读 ps:由于工作不定时繁忙,本人也无法定期更新,但是会尽量抽时间学习,分享给大家

我的收藏