zoukankan      html  css  js  c++  java
  • jQuery07源码 (3803 , 4299) attr() prop() val() addClass()等 : 对元素属性的操作

    var nodeHook, boolHook,
        rclass = /[	
    f]/g,
        rreturn = /
    /g,
        rfocusable = /^(?:input|select|textarea|button)$/i;
    
    jQuery.fn.extend({
        attr: function( name, value ) {
    //遍历this
    //arguments.length > 1,jQuery.attr(this[i],name,value),返回this
    //arguments.length <= 1,jQuery.attr(this[i],name,value),返回this
            return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
        },
    
        removeAttr: function( name ) {
            return this.each(function() {
                jQuery.removeAttr( this, name );
            });
        },
    
        prop: function( name, value ) {
            return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
        },
    
        removeProp: function( name ) {
            return this.each(function() {
                delete this[ jQuery.propFix[ name ] || name ];
            });
        },
    
        //$('.div1').addClass('box2 box3');
        addClass: function( value ) {
            var classes, elem, cur, clazz, j,
                i = 0,
                len = this.length,
                proceed = typeof value === "string" && value;//是字符串返回字符串,不是返回flase
            /*
                $('div').addClass( function(index){
                    alert(index);
                    return 'box'+index;
                });
            */
            if ( jQuery.isFunction( value ) ) {
                console.log(this);//这里的this是jQuery对象,Object { 0: <div#div1.box>, 1: <div#div2.box>, 2: <div#div3.box>, length: 3},通过return ( context || rootjQuery ).find('.div1')原生方法获得,里面每一个是节点对象不是jQuery对象
                return this.each(function( j ) {
                    console.log(this);//这里不是jQuery对象是dom节点对象,<div id='div1'></div>,<div id='div2'></div>,<div id='div3'></div>
                    console.log(jQuery( this ));//jQuery( this )是jQuery对象,Object { 0: <div#div1.box>, context: <div#div1.box>, length: 1 },Object { 0: <div#div2.box>, context: <div#div2.box>, length: 1 },Object { 0: <div#div3.box>, context: <div#div3.box>, length: 1 }
                    /*jQuery( this )走的是
                    if ( selector.nodeType ) {//节点都有nodeType属性
                    this.context = this[0] = selector;
                    this.length = 1;
                    return this;*/
                    jQuery( this ).addClass( value.call( this, j, this.className ) );
                });
            }
            if ( proceed ) {
                // 把字符串正则分割成数组
                classes = ( value || "" ).match( core_rnotwhite ) || [];
                for ( ; i < len; i++ ) {
                    elem = this[ i ];
                    //不是元素节点返回false,elem.className元素有没有class属性,有就合并(重复不合并),cur是之前的class
                    cur = elem.nodeType === 1 && ( elem.className ?
                    //非空格转换成空格
                        ( " " + elem.className + " " ).replace( rclass, " " ) :
                        " "
                    );
                    if ( cur ) {
                        j = 0;
                        while ( (clazz = classes[j++]) ) {
                            if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
                                cur += clazz + " ";
                            }
                        }//前后去空格
                        elem.className = jQuery.trim( cur );
                    }
                }
            }
            return this;
        },
    
        removeClass: function( value ) {
            var classes, elem, cur, clazz, j,
                i = 0,
                len = this.length,
                //先执行&&再||,proceed为true参数长度是0删除所有或者参数是字符串,为false传的不是字符串
                proceed = arguments.length === 0 || typeof value === "string" && value;
            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 ];
                    // This expression is here for better compressibility (see addClass)
                    cur = elem.nodeType === 1 && ( elem.className ?
                        ( " " + elem.className + " " ).replace( rclass, " " ) :
                        ""
                    );
                    if ( cur ) {
                        j = 0;
                        while ( (clazz = classes[j++]) ) {
                            // Remove *all* instances
                            while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
                                cur = cur.replace( " " + clazz + " ", " " );
                            }
                        }
                        elem.className = value ? jQuery.trim( cur ) : "";
                    }
                }
            }
            return this;
        },
    
        toggleClass: function( value, stateVal ) {
            var type = typeof value;
        //$('#div1').toggleClass('box2 box3',true);//有没有都是add
        //$('#div1').toggleClass('box2 box3',false);//有没有都是删除
            if ( typeof stateVal === "boolean" && type === "string" ) {//真就添加,假就删除
                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 );
                });
            }
            //$('#div1').toggleClass('box2 box3')
            return this.each(function() {
                if ( type === "string" ) {
                    // toggle individual class names
                    var className,
                        i = 0,
                        self = jQuery( this ),//转成jQuery对象,hasClass是jQuery对象的方法。
                        //空格分割成数组
                        classNames = value.match( core_rnotwhite ) || [];
    
                    while ( (className = classNames[ i++ ]) ) {
                        // check each className given, space separated list
                        if ( self.hasClass( className ) ) {
                            self.removeClass( className );
                        } else {
                            self.addClass( className );
                        }
                    }
    
                // Toggle whole class name
                //$('#div1').toggleClass(false);
                } else if ( type === core_strundefined || type === "boolean" ) {
                    if ( this.className ) {
                        // store className if set
                        data_priv.set( this, "__className__", this.className );
                    }
    
                    // If the element has a class name or if we're passed "false",
                    // then remove the whole classname (if there was one, the above saved it).
                    // Otherwise bring back whatever was previously saved (if anything),
                    // falling back to the empty string if nothing was stored.
                    this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
                }
            });
        },
    
        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;
        },
    
        val: function( value ) {
            var hooks, ret, isFunction,
                elem = this[0];
            //$('#input1').val()
            if ( !arguments.length ) {//获取
                if ( elem ) {//只获取第一个元素
                //hooks兼容处理,jQuery.valHooks[ elem.type ]在valHooks 这个json中找不到就找jQuery.valHooks[ elem.nodeName.toLowerCase() ]
                    hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
    /*
    valHooks: {
    hooks = option: {   //elem.type || elem.nodeName.toLowerCase()
                get: function( elem ) {}
            },
    hooks = select: {
                get: function( elem ) {},
                set: function( elem, value ) {}
            }
            下面的:
    hooks = radio: { 
                set: function( elem ) {}
                get: function( elem, value ) {}
            },
    hooks = checkbox: {
                set: function( elem ) {},
                get: function( elem, value ) {}
            }        
    }
    */
                    if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
                        return ret;
                    }
                    //不再hooks里面
                    ret = elem.value;
    
                    return typeof ret === "string" ?
                        // handle most common string cases
                        ret.replace(rreturn, "") :
                        // handle cases where value is null/undef or number
                        ret == null ? "" : ret;
                }
    
                return;
            }
            //设置
            isFunction = jQuery.isFunction( value );
    
            return this.each(function( i ) {
                var val;
                if ( this.nodeType !== 1 ) {
                    return;
                }
                if ( isFunction ) {
                    val = value.call( this, i, jQuery( this ).val() );
                } else {
                    val = value;
                }
                // Treat null/undefined as ""; convert numbers to string
                if ( val == null ) {//$('#input1').val(null);
                    val = "";
                } else if ( typeof val === "number" ) {//$('#input1').val(123123);
                    val += "";//转成字符串
                } else if ( jQuery.isArray( val ) ) {//$('#input2').val(['hello']);
                    val = jQuery.map(val, function ( value ) {
                        return value == null ? "" : value + "";
                    });
                }
                hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
                // If set returns undefined, fall back to normal setting
                if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
                    this.value = val;
                }
            });
        }
    });
    
    jQuery.extend({//静态属性只能通过jQuery静态方式调
        valHooks: {//option-get,select-get.select-set  兼容性处理
            option: {
                get: function( elem ) {
                    // attributes.value is undefined in Blackberry 4.7 but
                    // uses .value. See #6932
                    var val = elem.attributes.value;
                    //val不存在输出elem.value,val存在specified为false走elem.text
                    return !val || val.specified ? elem.value : elem.text;
                }
            },
            select: {
                //$('select').val()
                get: function( elem ) {
                    var value, option,
                        options = elem.options,//下拉选项
                        index = elem.selectedIndex,//当前索引值
                        //select只选了一个或者没有选,one为true,就是单选
                        one = elem.type === "select-one" || index < 0,
                        //one为true时单选values是空,one是false时多选values是一个数组存储所有的选择的多个
                        values = one ? null : [],
                        //单选时max是当前索引加1,多选时是下拉选项的长度
                        max = one ? index + 1 : options.length,
                        //
                        i = index < 0 ?
                            max /*index < 0没有选择时one=true,i=max=0*/ 
                            :one ? 
                            index/*index >= 0有选择时,select-one单选one=true,i=index,max=index+1,*/ 
                            :0   /*index >= 0有选择时,不是select-one多选one=false,i=0,max=options.length*/
                        ;
    
                    // 没有选择不进入循环,不获取select的val()
                    //有选择单选,i=index,只获取index的val()
                    //有选择好多选,全部获取
                    for ( ; i < max; i++ ) {
                        option = options[ i ];//js对象
    
                        // IE6-9 doesn't update selected after form reset (#2551)
                        if ( ( option.selected || i === index ) &&
                                // Don't return options that are disabled or in a disabled optgroup
                                ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
                                ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
    
                            // Get the specific value for the option
                            value = jQuery( option ).val();//转成jQuery对象
    
                            // We don't need an array for one selects
                            if ( one ) {
                                return value;
                            }
    
                            // Multi-Selects return an array
                            values.push( value );
                        }
                    }
    
                    return values;
                },
                //    $('#select').val(111);//111被选中了
                set: function( elem, value ) {
                    var optionSet, option,
                        options = elem.options,//所有的下拉选项,js对象
                        values = jQuery.makeArray( value ),//转成数组
                        i = options.length;
    
                    while ( i-- ) {//遍历
                        option = options[ i ];
                        if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {//在数组里面就把她设为选中
                            optionSet = true;
                        }
                    }
    
                    // force browsers to behave consistently when non-matching value is set
                    if ( !optionSet ) {//都没有
                        elem.selectedIndex = -1;
                    }
                    return values;
                }
            }
        },
    
        attr: function( elem, name, value ) {
            var hooks, ret,
                nType = elem.nodeType;
    
            // 节点不存在,或者文本、属性、注释节点
            if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
                return;
            }
    
            // core_strundefined = typeof undefined,
            if ( typeof elem.getAttribute === core_strundefined ) {
                //$(document).attr('title','hello'); 走这里通过.设置
                return jQuery.prop( elem, name, value );
            }
    
            // 1是元素节点,
            if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
                name = name.toLowerCase();
                //只有type才做兼容性处理
                hooks = jQuery.attrHooks[ name ] ||
                //$('input').attr('checked',true);//没问题,做兼容了
                    ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
            }
    
            if ( value !== undefined ) {//设置
    //$('#div1').attr('miaov',null); 调用remove
                if ( value === null ) {
                    jQuery.removeAttr( elem, name );
    //hooks中,set方法存在,就调用set方法并且返回值存在,就返回返回值
                } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
                    return ret;//有兼容性执行兼容操作,返回值
    
                } else {//没有兼容性操作设置值
                    elem.setAttribute( name, value + "" );
                    return value;
                }
    //hooks中,get方法存在,就调用get方法并且返回值存在,就返回返回值
            } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {//获取,有兼容性返回值
                return ret;
    
            } else {//获取没有兼容性时
                ret = jQuery.find.attr( elem, name );
    
                // Non-existent attributes return null, we normalize to undefined
                return ret == null ?
                    undefined :
                    ret;
            }
        },
    
        removeAttr: function( elem, value ) {
            var name, propName,
                i = 0,
                //$('#div1').removeAttr('maio href id');
                attrNames = value && value.match( core_rnotwhite );//core_rnotwhite = /S+/g, 非空格,返回数组
    
            if ( attrNames && elem.nodeType === 1 ) {
                while ( (name = attrNames[i++]) ) {
                    /*
                    propFix: {
                        "for": "htmlFor",
                        "class": "className"
                    },
                    *///  $('#div1').removeAttr('class');
                    propName = jQuery.propFix[ name ] || name;
    
                    // Boolean attributes get special treatment (#10870)
                    if ( jQuery.expr.match.bool.test( name ) ) {
                        // $('#div1').removeAttr('checked');
                        elem[ propName ] = false;
                    }
    
                    elem.removeAttribute( name );//调用原生
                }
            }
        },
    //hooks = jQuery.attrHooks[ name ]
        attrHooks: {
            type: {//只有name = 'type',才会有有兼容性判断。
                set: function( elem, value ) {//只有set说明兼容只是针对设置没有获取
                    if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {//单选值的兼容
                        // Setting the type on a radio button after the value resets the value in IE6-9
                        // Reset value to default in case type is set after value during creation
                        
                        //当设置type = 'radio'时IE会有兼容性问题,所以要先设置类型才设置值
                        var val = elem.value;
                        elem.setAttribute( "type", value );
                        if ( val ) {
                            elem.value = val;
                        }
                        return value;
                    }
                }
            }
        },
    
        propFix: {
            "for": "htmlFor",
            "class": "className"
        },
    
        prop: function( elem, name, value ) {
            var ret, hooks, notxml,
                nType = elem.nodeType;
    
            // don't get/set properties on text, comment and attribute nodes
            if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
                return;
            }
    
            notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
    
            if ( notxml ) {
                // Fix name and attach hooks
                name = jQuery.propFix[ name ] || name;
                hooks = jQuery.propHooks[ name ];//兼容性处理
            }
    
            if ( value !== undefined ) {//设置值
                return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
                    ret :
                    ( elem[ name ] = value );//prop使用的是.操作
    
            } else {//获取值
                return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
                    ret :
                    elem[ name ];
            }
        },
    
        propHooks: {
            tabIndex: {//光标切换顺序,只对tabIndex属性做兼容
                get: function( elem ) {//只对get方法做兼容
                    return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
                        elem.tabIndex :
                        -1;
                }
            }
        }
    });
    
    // Hooks for boolean attributes
    boolHook = {
        set: function( elem, value, name ) {
            if ( value === false ) {
                // Remove boolean attributes when set to false
                jQuery.removeAttr( elem, name );
            } else {
                elem.setAttribute( name, name );
            }
            return name;
        }
    };
    jQuery.each( jQuery.expr.match.bool.source.match( /w+/g ), function( i, name ) {
        var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
    
        jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) {
            var fn = jQuery.expr.attrHandle[ name ],
                ret = isXML ?
                    undefined :
                    /* jshint eqeqeq: false */
                    // Temporarily disable this handler to check existence
                    (jQuery.expr.attrHandle[ name ] = undefined) !=
                        getter( elem, name, isXML ) ?
    
                        name.toLowerCase() :
                        null;
    
            // Restore handler
            jQuery.expr.attrHandle[ name ] = fn;
    
            return ret;
        };
    });
    
    // Support: IE9+
    // Selectedness for an option in an optgroup can be inaccurate
    if ( !jQuery.support.optSelected ) {
        jQuery.propHooks.selected = {
            get: function( elem ) {
                var parent = elem.parentNode;
                if ( parent && parent.parentNode ) {
                    parent.parentNode.selectedIndex;
                }
                return null;
            }
        };
    }
    
    jQuery.each([
        "tabIndex",
        "readOnly",
        "maxLength",
        "cellSpacing",
        "cellPadding",
        "rowSpan",
        "colSpan",
        "useMap",
        "frameBorder",
        "contentEditable"
    ], function() {
        //value = callback.call( obj[ i ], i, obj[ i ] );
        jQuery.propFix[ this.toLowerCase() ] = this;
    });
    
    /*
    valHooks: {
    hooks = radio: { 
                set: function( elem ) {}
                get: function( elem, value ) {}
            },
    hooks = checkbox: {
                set: function( elem ) {},
                get: function( elem, value ) {}
            }
    }
    */
    jQuery.each([ "radio", "checkbox" ], function() {
        jQuery.valHooks[ this ] = {
            //$('#radio').val(['hello']);
            set: function( elem, value ) {
                if ( jQuery.isArray( value ) ) {
                    return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );//设置选中状态
                }
            }
        };
        if ( !jQuery.support.checkOn ) {
        //有的话做处理,没有不做处理    //获取单选框和复选框的value值时绝大多数浏览器返回的都是on,有些是空的,
            jQuery.valHooks[ this ].get = function( elem ) {
                // Support: Webkit
                // "" is returned instead of "on" if a value isn't specified
                return elem.getAttribute("value") === null ? "on" : elem.value;
            };
        }
    });
  • 相关阅读:
    POJ 3169 Layout(差分约束+链式前向星+SPFA)
    HDU 2680 Choose the best route(SPFA)
    PAT L2-016 愿天下有情人都是失散多年的兄妹(深搜)
    PAT L2-013 红色警报(并查集求连通子图)
    PAT L2-014 列车调度(最长上升nlogn)
    PAT L3-010 是否完全二叉搜索树(二叉搜索树)
    HRBUST 2310 Tree Painting(无向图欧拉路径的性质)
    POJ 2230 Watchcow(有向图欧拉回路)
    UVa 10054 The Necklace(无向图欧拉回路)
    UVa 1600 Patrol Robot(三维广搜)
  • 原文地址:https://www.cnblogs.com/yaowen/p/6935537.html
Copyright © 2011-2022 走看看