zoukankan      html  css  js  c++  java
  • jQuery源码分析系列:属性操作

    属性操作

    1.6.1相对1.5.x最大的改进,莫过于对属性.attr()的重写了。在1.6.1中,将.attr()一分为二: .attr()、.prop(),这是一个令人困惑的变更,也是一个破坏性的升级,会直接影响到无数的网站和项目升级到1.6。

    简单的说,.attr()是通过setAttribute、getAttribute实现,.prop()则通过Element[ name ]实现:

    jQuery.attr

    setAttribute, getAttribute

    jQuery.removeAttr

    removeAttribute, removeAttributeNode(getAttributeNode )

    jQuery.prop

    Element[ name ]

    jQuery.removeProp

    delete Element[ name ]

     

    事实上.attr()和.prop()的不同,是HTML属性(HTML attributes)和DOM属性(DOM properties)的不同。HTML属性解析的是HTML代码中的存在的属性,返回的总是字符串,而DOM属性解析的是DOM对象的属性,可能是字符串,也可能是一个对象,可能与HTML属性相同,也可能不同。

    1 <a href="abc.html" class="csstest" style="font-size: 30px;">link</a>
    2 
    3 <input type="text" value="123">
    4 
    5 <input type="checkbox" checked="checked">

    javascript代码:

     1 console.info( $('#a').attr('href') ); // abc.html
     2 
     3 console.info( $('#a').prop('href') ); // file:///H:/open/ws-nuysoft/com.jquery/jquery/abc.html
     4 
     5  
     6 console.info( $('#a').attr('class') ); // csstest
     7 console.info( $('#a').prop('class') ); // csstest
     8 
     9 console.info( document.getElementById('a').getAttribute('class') ); // csstest
    10 
    11 console.info( document.getElementById('a').className ); // csstest
    12 
    13 console.info( $('#a').attr('style') ); // font-size: 30px;
    14 
    15 console.info( $('#a').prop('style') ); // CSSStyleDeclaration { 0="font-size", fontSize="30px", ...}
    16 
    17 console.info( document.getElementById('a').getAttribute('style') ); // font-size: 30px;
    18 
    19 console.info( document.getElementById('a').style ); // CSSStyleDeclaration { 0="font-size", fontSize="30px", ...}
    20 
    21 console.info( $('#text').attr('value') ); // 123
    22 console.info( $('#text').prop('value') ); // 123
    23 
    24 console.info( $('#checkbox').attr('checked') ); // checked
    25 console.info( $('#checkbox').prop('checked') ); // true
    26 
    27  

    不同之处总结如下:

      1.属性名可能不同,尽管大部分的属性名还是相似或一致的。

      2.HTML属性值总是返回字符串,DOM属性值则可能是整型、字符串、对象,可以获取更多的内容

      3.DOM属性总是返回当前的状态(值),而HTML属性(在大多数浏览)返回的初始化时的状态(值)

      4.DOM属性只能返回固定属性名的值,而HTML属性则可以返回在HTML代码中自定义的属性名的值

      5.相对于HTML属性的浏览器兼容问题,DOM属性名和属性值在浏览器之间的差异更小,并且DOM属性也有标准可依

    优先使用.prop(),因为.prop()总是返回最新的状态(值)

    从源码可以看出.attr()的处理过程,先特殊处理各种特殊情况,再用约定getAttribute()和setAttribute()方法

    jQuery.attr()源码:

      .attr(attributeName) 取得第一个匹配元素的属性值(当属性没有被设置时,返回undefined,不能用在文本节点、注释节点、属性节点上)

      .attr(attributeName, value) 设置单个属性

      .attr(map) 设置多个属性

      .attr(attributeName, function(index, attr)) 通过函数的返回值设置属

     1     //工具方法  设置或获取HTML元素  setAttribute和getAttribute实现        
     2     attr: function( elem, name, value, pass ) {
     3             var nType = elem.nodeType;
     4             //节点类型不能是:文本 注释 属性节点
     5             if(!elem || nType === 3 || nType === 8 nType === 2){
     6                 return undefined;
     7             }
     8             //1>>遇到与方法同名的属性 则执行方法
     9             //2>>遇到扩展或需要修正的属性 执行相应的方法
    10             //判断属性名是否在jQuery提供的方法jQuery.attrFn中,是则直接调用方法。
    11             if(pass && name in jQuery.attrFn){//属性方法
    12                 return jQuery(elem)[name](value);
    13             }
    14             //如果不支持getAttribute 则调用$.prop()方法
    15             if(!("getAttribute" in elem)){
    16                 return jQuery.prop(elem,name,value);
    17             }
    18             var ret,hooks,
    19                 notxml = nType !==1 || !jQuery.isXMLDoc(elem);//判断documentElement是否存在
    20 
    21             //格式化name      attrFix: { tabindex: "tabIndex"}
    22             name = notxml && jQuery.attrFix[name] || name;
    23             //属性钩子:type:  tabIndex
    24             hooks = jQuery.attrHooks[name];
    25             //如果没有name对应的钩子
    26             if(!hooks){
    27                 //使用boolean钩子处理boolean属性
    28                 (typeof value === "boolean" || value === undefined || value.tolowerCase() === name.toLowerCase())){
    29                     //使用布尔钩子(静态方法对象):set get
    30                     hooks = boolHook;
    31                 //使用表单钩子
    32                 }else if (formHook && (jQuery.nodeName(elem,"form") || rinvalidChar.test(name))){
    33                 //使用表单钩子(静态方法对象):set get
    34                 hooks = formHook;
    35                 }
    36             }
    37 
    38             //定义了value   设置或删除
    39             if(value !== undefined){
    40             /*
    41                 typeof null === 'object'   true
    42                 typeof undefined === 'undefined'  true
    43                 null == undefined   true
    44                 null === undefined  false
    45             */
    46                 if(value === null){//有值但是为空 即将值设置为空
    47                     jQuery.reomveAttr(elem,name);
    48                     return undefined;
    49                 //属性钩子 布尔钩子 表单钩子 如果有对象的钩子 就调用set方法
    50                 }else if(hooks && "set" in hooks && noxml && (ret = hooks.set(elem,value,name))!==undefeind){
    51                     return ret;
    52                 }else{
    53                     //调用setAttribute方法
    54                     elem.setAttribute(name,"" + value);
    55                     return value;
    56                 }
    57             //value是undefined,说明去属性 ,存在对应钩子有get方法,调用钩子的get方法
    58             }else if(hooks && "get" in hooks && notxml){
    59                 return hooks.get(elem,name);
    60             }else{
    61                 //取属性值
    62                 ret = elem.getAttribute(name);
    63                 //属性不存返回null  格式化为undefined
    64                 return ret === null ? undefined : ret;
    65             }
    66         },

    其中调用的钩子有些不明白的地方:

    attrHooks源码:如果name = type属性,则加判断后调用setAttribute设置属性值,或者name = tabIndex时通过getAttributeNode("tabIndex")获得//获得自定义对象属性.

        //属性钩子 name = type || tabIndex
        attrHooks: {
            type: {
                set: function( elem, value ) {
                    //type属性在IE下不能改变 如果改变报错
                    if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
                        jQuery.error( "type property can't be changed" );
                    } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
                        var val = elem.value;
                        elem.setAttribute( "type", value );//最后调用setAttribute设置type属性的值
                        if ( val ) {
                            elem.value = val;
                        }
                        return value;
                    }
                }
            },
            tabIndex: {
                get: function( elem ) {
                    //获得tabIndex的值
                    var attributeNode = elem.getAttributeNode("tabIndex");//获得自定义对象属性
                    return attributeNode && attributeNode.specified ?
                        parseInt( attributeNode.value, 10 ) ://获得自定义对象属性的值
                        rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
                            0 :
                            undefined;
                }
            }
        },

    AttrFn源码:如果name在这个属性方法的对象中,直接调用这个属性方法。

          //取属性用到的方法
          attrFn: {
            val: true,
            css: true,
            html: true,
            text: true,
            data: true,
             true,
            height: true,
            offset: true
        },

    boolHook源码:

     1     //bool钩子
     2     boolHook = {
     3         get: function( elem, name ) {
     4             // Align boolean attributes with corresponding properties
     5             return elem[ jQuery.propFix[ name ] || name ] ?
     6                 name.toLowerCase() :
     7                 undefined;
     8         },
     9         set: function( elem, value, name ) {
    10             var propName;
    11             if ( value === false ) {//value不存在时,删除boolean属性
    12                 jQuery.removeAttr( elem, name );
    13             } else {
    14                 //属性名 是否在propFix中,存在就直接调用
    15                 propName = jQuery.propFix[ name ] || name;
    16                 if ( propName in elem ) {
    17                     elem[ propName ] = value;
    18                 }
    19                 //propName不在elem中就用setAttribute
    20                 elem.setAttribute( name, name.toLowerCase() );
    21             }
    22             return name;
    23         }
    24     };

    formHook源码:设置表单钩子,get/set。

    $.prop()源码:

     1         //获取DOM属性
     2         prop: function( elem, name, value ) {
     3             var nType = elem.nodeType;
     4             //节点类型不能是:文本 注释 属性节点
     5             if(!elem || nType === 3 || nType === 8 nType === 2){
     6                 return undefined;
     7             }
     8             var ret,hooks,
     9                 notxml = nType !==1 || !jQuery.isXMLDoc(elem);//判断documentElement是否存在
    10 
    11             //格式化name      attrFix: { tabindex: "tabIndex"}
    12             name = notxml && jQuery.attrFix[name] || name;
    13             //属性钩子:name = type || tabIndex
    14             hooks = jQuery.attrHooks[name];
    15             if(value !== undefined){
    16                 //如果钩子存在set  调用set方法
    17                 if(hooks && "set" && (ret = hooks.set(elem,value,name))!==undefined){
    18                     return ret;
    19                 }else{
    20                     return (elem[name] = value);
    21                 }
    22             //读取
    23             }else{
    24                 if(hooks && "get" && (ret = hooks.get(elem,value,name))!==undefined){
    25                     return ret;
    26                 }else{
    27                     return elem[name];
    28                 }
    29             }
    30         },

    jQuery中调用access方法实现jQuery.fn.attr和jQuery.fn.prop:

    1   attr: function( name, value ) {
    2       return jQuery.access( this, name, value, true, jQuery.attr );
    3   },
    4 
    5   prop: function( name, value ) {
    6       return jQuery.access( this, name, value, true, jQuery.prop );
    7   }

    $.access方法源码:

    多功能函数:读取或设置集合的属性值;值为函数时会被执行
      1>>elems:元素的集合,【collection】【类】数组
      2>>key:属性名称,key的只为object时,会拆解key为key,value形式再次执行jQuery.access
      3>>value:属性值
      4>>exec:在属性值为function时是否对设置之前的value值执行函数(这里为true)
      5>>fn:执行的函数
      6>>pass:是否设置为jQuery对象的属性  attr时使用
      

      用于fn:jQuery.fn.css(),jQuery.fn.attr(),jQueyr.fn.prop
      return jQuery.access( this, name, value, true, function( elem, name, value ) {});

    说明:主要用于参数的循环处理,为什么要这样做?待详细分析

    access:function(elems,key,value,exec,fn,pass){
                    var elems = elems.length;
                    //如果有多个属性就迭代
                    if(typeof key === "object"){
                        for(var k in key){//key的参数循环处理
                            jQuery.access(elems,k,key[k],exec,fn,value);
                        }
                        return elems;
                    }
                    //只设置一个属性
                    if(value !== undefined){
                        exec = !pass && exec && jQuery.isFunction(value);
    
                        for (var i=0;i<length;i++){
                            fn(elems[i],key, exec ? value.call(elems[i],i,fn(elems[i],key)) : value,pass);
                        }
                        return elems;
                    }
                    //读取属性
                    return length ? fn(elems[0],key) : undefined;
    
                },
  • 相关阅读:
    Celery详解
    JWT详解
    进程及进程池
    多线程详解
    python常用模块之os模块的用法
    python常用模块之paramiko与ssh
    reflect 反射
    http 静态文件
    模板渲染语言
    http web 开发
  • 原文地址:https://www.cnblogs.com/colorstory/p/2612959.html
Copyright © 2011-2022 走看看