jQuery attributes(下)

前文对属性的设置、读取、删除方法做了分解,本文继续对jQuery attributes模块分解。

jQuery.fn.addClass

/************************************
 *    value: 字符串或者是函数,字符串可以通过空格分隔className
 */
jQuery.fn.addClass = function( value ) {
    var classes, elem, cur, clazz, j,
        i = 0,
        len = this.length,
        proceed = typeof value === "string" && value;

    //如果value是函数
    if ( jQuery.isFunction( value ) ) {
        //则对所有元素迭代运行addClass
        return this.each(function( j ) {
            jQuery( this ).addClass( value.call( this, j, this.className ) );
        });
    }

    //如果value是字符串
    if ( proceed ) {
        // 对value字符串分割成数组
        classes = ( value || "" ).match( core_rnotwhite ) || [];

        //遍历元素
        for ( ; i < len; i++ ) {
            elem = this[ i ];
            //如果节点是元素,则获取原来的className
            cur = elem.nodeType === 1 && ( elem.className ?
                ( " " + elem.className + " " ).replace( rclass, " " ) :    //替换掉换行符制表符等
                " "
            );

            //如果cur不为false,即节点是元素
            if ( cur ) {
                j = 0;
                //遍历classes组装成新的className应有的值
                while ( (clazz = classes[j++]) ) {
                    if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
                        cur += clazz + " ";
                    }
                }
                //对className赋值,去掉头尾空白
                elem.className = jQuery.trim( cur );

            }
        }
    }

    return this;
};

添加class的实现上还是比较简单的,利用elem.className来赋值。需要注意:

var rclass = /[\t\r\n]/g;

jQuery.fn.removeClass

jQuery.fn.removeClass = function( value ) {
    var classes, elem, cur, clazz, j,
        i = 0,
        len = this.length,
        //参数是否正确
        proceed = arguments.length === 0 || typeof value === "string" && value;

    //如果value是函数
    if ( jQuery.isFunction( value ) ) {
        //则对所有元素迭代运行removeClass
        return this.each(function( j ) {
            jQuery( this ).removeClass( value.call( this, j, this.className ) );
        });
    }
    
    //如果参数正确
    if ( proceed ) {
        //分隔value成为class字符串数组
        classes = ( value || "" ).match( core_rnotwhite ) || [];

        //遍历
        for ( ; i < len; i++ ) {
            elem = this[ i ];
            // 获取className并进行预处理
            cur = elem.nodeType === 1 && ( elem.className ?
                ( " " + elem.className + " " ).replace( rclass, " " ) :
                ""
            );

            //如果是元素
            if ( cur ) {
                j = 0;
                //遍历所有class字符串
                while ( (clazz = classes[j++]) ) {
                    // 寻找是否有对应的字符串
                    while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
                        //有则去掉
                        cur = cur.replace( " " + clazz + " ", " " );
                    }
                }
                //给className赋值,并去掉头尾空格
                elem.className = value ? jQuery.trim( cur ) : "";
            }
        }
    }

    return this;
};

删除class的实现和addClass非常像,只是通过indexOf和replace来替换掉需要删除的class。

jQuery.fn.toggleClass

jQuery.fn.toggleClass = function( value, stateVal ) {
    var type = typeof value,
        isBool = typeof stateVal === "boolean";

    //(⊙o⊙)…不说了,大家懂得
    if ( jQuery.isFunction( value ) ) {
        return this.each(function( i ) {
            jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
        });
    }

    //遍历所有元素
    return this.each(function() {
        //如果value是字符串
        if ( type === "string" ) {
            var className,
                i = 0,
                self = jQuery( this ),
                state = stateVal,
                //将value转成classNames字符串数组
                classNames = value.match( core_rnotwhite ) || [];

            //遍历
            while ( (className = classNames[ i++ ]) ) {
                //stateVal是布尔量,则直接设置为stateVal,否则判断元素是否不存在该className
                state = isBool ? state : !self.hasClass( className );
                //如果该className不存在则添加,否则删除
                self[ state ? "addClass" : "removeClass" ]( className );
            }

        // 如果value的类型是undefined或者boolean
        } else if ( type === "undefined" || type === "boolean" ) {
            //如果元素的className存在
            if ( this.className ) {
                // 将其存入缓存
                jQuery._data( this, "__className__", this.className );
            }

            //对className赋值,为空或者缓存中的值
            this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
        }
    });
};

为了实现jQuery.fn.toggleClass还是花了很大功夫的。

缓存的利用使得toggleClass操作更加方便,而不需要记录以前是那些class。

jQuery.fn.hasClass

jQuery.fn.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 ) >= 0 ) {
            return true;
        }
    }

    return false;
};

这个函数通过indexOf来寻找className是否存在。

jQuery.fn.val

jQuery.fn.val = function( value ) {
    var hooks, ret, isFunction,
        elem = this[0];

    //如果没有参数
    if ( !arguments.length ) {
        //如果元素存在
        if ( elem ) {
            //得到相应的钩子
            hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];

            //通过钩子来得到值
            if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
                return ret;
            }

            //如果没得到钩子,则通过elem.value来返回值
            ret = elem.value;

            //如果ret是字符串
            return typeof ret === "string" ?
                // 将回车符替换
                ret.replace(rreturn, "") :
                // 如果ret是空的,则返回"",否则返回ret
                ret == null ? "" : ret;
        }

        return;
    }

    //value是否是函数
    isFunction = jQuery.isFunction( value );

    //遍历所有元素
    return this.each(function( i ) {
        var val,
            self = jQuery(this);

        if ( this.nodeType !== 1 ) {
            return;
        }

        //如果value是函数,则转成参数
        if ( isFunction ) {
            val = value.call( this, i, self.val() );
        } else {
            val = value;
        }

        // 将null/undefined当成""
        if ( val == null ) {
            val = "";
        // 将数字转成字符串
        } else if ( typeof val === "number" ) {
            val += "";
        //如果是数组,则遍历数组
        } else if ( jQuery.isArray( val ) ) {
            val = jQuery.map(val, function ( value ) {
                return value == null ? "" : value + "";
            });
        }

        //获取相应钩子
        hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

        // 如果钩子无法设置,则使用通常的设置方法
        if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
            this.value = val;
        }
    });
};
原文地址:https://www.cnblogs.com/justany/p/2867800.html