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;
            };
        }
    });
  • 相关阅读:
    Java核心技术 卷一 笔记四 库类的直接使用
    Java核心技术 卷一 笔记三 大数值及数组
    Java核心技术 卷一 笔记2 字符串的复制
    Java核心技术 卷一 笔记1
    修改css 样式后, hover事件 不生效
    修改 element ui input 输入框 样式不生效问题
    css3 计算属性
    Vue3 改动系列
    浏览器实现,向下滑动 鼠标滚轮,页面横向移动
    linux ceont0s7 vue 打包压缩图片 一直报错
  • 原文地址:https://www.cnblogs.com/yaowen/p/6935537.html
Copyright © 2011-2022 走看看