zoukankan      html  css  js  c++  java
  • jQuery-1.9.1源码分析系列(八) 属性操作

      jQuery的属性操作主要包括

      jQuery.fn.val

      jQuery.fn.attr

      jQuery.fn.removeAttr

      jQuery.fn.prop

      jQuery.fn.removeProp

      jQuery.fn.addClass

      jQuery.fn.removeClass

      jQuery.fn.toggleClass

      

      接下来一一分析jQuery对他们的处理

    a. jQuery.fn.val


      jQuery.fn.val用来获取jQuery对象的第一个元素的val值或者给jQuery对象的每一个元素设置其val值。参数个数为0表示获取get,否则表示设置set。

      处理过程比较简单:

      判断参数个数,没有参数表示获取当前匹配元素中的第一个元素的value值,此时如果能使用valHooks则使用之,否则使用elem.value获取值(null/undefined需要转成空字符"");

      如果有参数,表示要为当前所有的匹配元素设置值,如果参数是函数,调用函数的返回值作为值val,否则使用传递的参数做为值val。能使用则用之,否则使用elem.value = val。

      源码:

    val: function( value ) {
        var ret, hooks, isFunction,
        elem = this[0];//获取jQuery对象的第一个元素
        //获取值
        if ( !arguments.length ) {
            if ( elem ) {
           hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
                //通过hooks如果能取到值则返回
                if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
                    return ret;
                }
                //否则正常取值
                ret = elem.value;
                return typeof ret === "string" ?
                        // 换行符转换成空字符
                        ret.replace(rreturn, "") :
                        //处理null/undef 或数字
                        ret == null ? "" : ret;
            }
            return;
        }
    
        isFunction = jQuery.isFunction( value );
        //对jQuery对象的每一个元素设置val
        return this.each(function( i ) {
            var val,
            self = jQuery(this);
            //元素不为DOM节点则返回
            if ( this.nodeType !== 1 ) {
                return;
            }
    
            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() ];
    
            //如果hooks的set返回为undefined,则使用正常设置
            if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
                this.value = val;
            }
        });
    }
    View Code

    b. jQuery.fn.attr  


      设置或返回当前jQuery对象所匹配的元素节点的属性值。

            attr: function( name, value ) {
                return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
            }

      当前匹配的元素挨个执行jQuery.attr,并返回执行结果集。

      $(...).attr的最基础api函数jQuery.attr。关键代码如下,其他省略。深度理解钩子机制

    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
    // All attributes are lowercase
    // Grab necessary hook if one is defined
    if ( notxml ) {
      name = name.toLowerCase();
      //查找hook
      hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
    }
    if ( value !== undefined ) {
      if ( value === null ) {
        jQuery.removeAttr( elem, name );
      //如果有hooks,就使用hooks来处理
      } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
        return ret;
      } else {…}
    //如果有hooks,就使用hooks来处理 } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {   return ret; } else {…}

    c. jQuery.fn.removeAttr


      用于移除在当前jQuery对象所匹配的每一个元素节点上指定的属性  

        removeAttr: function( name ) {
            return this.each(function() {
                jQuery.removeAttr( this, name );
            });
        }

      内部使用低级API jQuery.removeAttr实现

      底层使用elem.removeAttribute来实现。不过需要注意的是因为可能根本就没有要删除的属性,所以在删除之前都设置了该属性

                    // Boolean attributes get special treatment (#10870)
                    if ( rboolean.test( name ) ) {
                        // 如果是bool类型的属性(attribute)设置相应的特性(property)
                        //同时清除defaultChecked/defaultSelected (if appropriate) for IE<8
                        if ( !getSetAttribute && ruseDefault.test( name ) ) {
                            elem[ jQuery.camelCase( "default-" + name ) ] =
                            elem[ propName ] = false;
                        } else {
                            elem[ propName ] = false;
                        }
    
                    // See #9699 for explanation of this approach (setting first, then removal)
                    } else {
                        jQuery.attr( elem, name, "" );
                    }

      完整的jQuery.removeAttr源码如下

    removeAttr: function( elem, value ) {
      var name, propName,
      i = 0,
      attrNames = value && value.match( core_rnotwhite );
      if ( attrNames && elem.nodeType === 1 ) {
        while ( (name = attrNames[i++]) ) {
          propName = jQuery.propFix[ name ] || name;
    
          // Boolean attributes get special treatment (#10870)
          if ( rboolean.test( name ) ) {
            // 如果是bool类型的属性(attribute)设置相应的特性(property)
            //同时清除defaultChecked/defaultSelected (if appropriate) for IE<8
            if ( !getSetAttribute && ruseDefault.test( name ) ) {
              elem[ jQuery.camelCase( "default-" + name ) ] = elem[ propName ] = false;
            } else {
               elem[ propName ] = false;
            }
    
          // See #9699 for explanation of this approach (setting first, then removal)
          } else {
            jQuery.attr( elem, name, "" );
          }
    
          elem.removeAttribute( getSetAttribute ? name : propName );
        }
      }
    }
    View Code

      

    d. jQuery.fn.prop


      在分析这个函数之前先说一点必备知识。

      attribute和property的区别

      1)      property是对象的属性值(有的时候文章中也称为特征值,他们是相同的),通过elem[ name ]来取值/赋值; 而attribute是直接写在标签上的属性,通过elem.getAttribute /elem.setAttribute。观察一张图很直观的理解(引用Aaron的图例

     attributes是一个类数组的容器,说得准确点就是NameNodeMap,总之就是一个类似数组但又和数组不太一样的容器。attributes的每个数字索引以名值对(name=”value”)的形式存放了一个attribute节点。attributes是会随着添加或删除attribute节点动态更新的。property就是一个属性,如果把DOM元素看成是一个普通的Object对象,那么property就是一个以名值对(name=”value”)的形式存放在Object中的属性。要添加和删除property也简单多了,和普通的对象没啥分别。之所以attribute和property容易混倄在一起的原因是,很多attribute节点还有一个相对应的property属性

      2)      boolen类型的attr值更改是通过prop(elem[ propName ] = false;)方式来处理的,因为properties就是浏览器用来记录当前值的东西,boolean properties保持最新。但相应的boolean attributes是不一样的,它们仅被浏览器用来保存初始值。

      jQuery.fn.prop内部使用低级API jQuery.prop实现

    prop: function( name, value ) {
            return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
        },

      jQuery.prop的核心代码如下,其他代码省略

    if ( notxml ) {
      // Fix name and attach hooks
      name = jQuery.propFix[ name ] || name;
      //查找hook
      hooks = jQuery.propHooks[ name ];
    }
    if ( value !== undefined ) {
      //如果有hooks,就使用hooks来处理
      if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
        return ret;
      } else {
            return ( elem[ name ] = value );
      }
    } else {
      //如果有hooks,就使用hooks来处理
      if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
        return ret;
      } else {
        return elem[ name ];
      }
    }

    e. jQuery.fn.removeProp


      jQuery.fn.removeProp比较简单,就如同普通的js对象一样删除某个属性直接使用delete即可

    propFix: {
      tabindex: "tabIndex",
      readonly: "readOnly",
      "for": "htmlFor",
      "class": "className",
      maxlength: "maxLength",
      cellspacing: "cellSpacing",
      cellpadding: "cellPadding",
      rowspan: "rowSpan",
      colspan: "colSpan",
      usemap: "useMap",
      frameborder: "frameBorder",
      contenteditable: "contentEditable"
    }
    removeProp: function( name ) {
            name = jQuery.propFix[ name ] || name;
            return this.each(function() {
                //try/catch handles cases where IE balks (such as removing a property on window)
                try {
                    //删除prop处理
                    this[ name ] = undefined;
                    delete this[ name ];
                } catch( e ) {}
            });
        }

    f. jQuery.fn.addClass


      这个函数处理比较简单,将参数字符串使用空格分隔(多个class)为classes,将原来的ClassName获取出来为cur,将分classes添加到cur中即可(在此过程中需要保证classes[i]在cur中不存在即可)

      重点源码

                   //使用空格符分隔参数 
             classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ];
                //获取原来的class 名称 cur
    = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : " " ); if ( cur ) { j = 0;
                  //class名称组合
    while ( (clazz = classes[j++]) ) {
                    //保证class名称的唯一性
    if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } elem.className = jQuery.trim( cur ); } }

    g. jQuery.fn.removeClass


      这个函数也比较简单了,这里不分析了

    h. jQuery.fn.toggleClass


      这个函数只需要在调用.addClass和.removeClass之间切换即可。toggleClass因为可能需要来回切换的原因,需要保存原来的class,以便下次调用的时候恢复回来。不过需要注意的是当没有传递参数时,被认为是整个Class的切换,需要保存原来的class,以便下次调用的时候恢复回来,关键代码如下

    // Toggle whole class name
    else if ( type === core_strundefined || type === "boolean" ) {
        if ( this.className ) {
        jQuery._data( this, "__className__", this.className );
        }
    
    this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
    }

      完整源码  

            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.match( core_rnotwhite ) || [];
    
                        while ( (className = classNames[ i++ ]) ) {
                            // check each className given, space separated list
                            state = isBool ? state : !self.hasClass( className );
                            self[ state ? "addClass" : "removeClass" ]( className );
                        }
    
                    // Toggle whole class name
                    } else if ( type === core_strundefined || type === "boolean" ) {
                        if ( this.className ) {
                                // store className if set
                                jQuery._data( 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 ? "" : jQuery._data( this, "__className__" ) || "";
                    }
                });
            },
    View Code
     
  • 相关阅读:
    Python中的类(上)
    Django REST Framework API Guide 07
    Django REST Framework API Guide 06
    Django REST Framework API Guide 05
    Django REST Framework API Guide 04
    Django REST Framework API Guide 03
    Django REST Framework API Guide 02
    Django REST Framework API Guide 01
    Django 详解 信号Signal
    Django 详解 中间件Middleware
  • 原文地址:https://www.cnblogs.com/chuaWeb/p/jQuery-1-9-1-Attr-Prop-Class-Val.html
Copyright © 2011-2022 走看看