首页 > 代码库 > 对jquery的 attr()和prop()理解

对jquery的 attr()和prop()理解

jquery1.6版本后引入了prop()方法,很多时候总是混淆attr()与prop()这俩,下面分析一下这俩的区别

在此之前,先来了解一下html 的attribute和property,因为jquery的attr()和prop()正是来源于这俩东西。先看一段html代码:

<span  id="testId" class="testClass" selfAttribute="selfValue"></span>

根据 DOM (HTML) 规范,SPAN 元素在页面中生成一个相对应的对象,对象有一个标识为id的attribute,对于attribute,我们称为HTML标签属性,对于property,为DOM对象属性,注意下面这句话:

HTML 标签的属性会将其值暴露给对应的 DOM 对象的属性,如 HTML 元素的 id 属性与其对应的 DOM 对象的 id 属性会保持一种同步关系

所以该元素的dom对象中也会生成key为id的 property,所以可以通过key直接获得值,看下面的例子

var testId =  document.getElementById("testId");console.log(testId.id);//testIdconsole.log(testId.getAttribute("id"));//testId

这样一来是不是以后获取属性值都直接用.访问就可以了,下面继续看:

 var testId =  document.getElementById("testId"); console.log(testId.class);//undefinde console.log(testId.getAttribute("class"));//testClass

当我们直接访问class时,并没有得到我们期望的testClass,原因是在 HTML 标签中使用 class 属性指定元素使用的 CSS 类,但在 DOM 对象中由于 class 是 ECMAScript 的保留字,所以改用 className 属性,所以想直接获取class值,修改一下即可:

console.log(testId.className);//testClass

上面的例子可以知道 ,对于部分 "property" 与 "attribute" 在名称及值类型上是统一的,对于我们经常用到的自定义属性,可以继续做个测试:

 var testId =  document.getElementById("testId"); console.log(testId.selfAttribute);//undefined console.log(testId.getAttribute("selfAttribute"));//selfValue

testId.selfAttribute为undefined说明property与attribute并不共享自定义属性,引用别人的一张图来表示一下:

    

对于build-in区域的属性,property和attribute是共享的,比如id,但是IE6、7没有作区分,依然共享自定义属性数据,因为IE 混淆了 DOM 对象属性(property)及 HTML 标签属性(attribute),造成了对 setAttribute、getAttribute 的不正确实现,参考http://www.w3help.org/zh-cn/causes/SD9006

对于自定义的属性两者互不干涉,即使name是相同的,看下面例子:

 

 var testId =  document.getElementById("testId");       testId.setAttribute("stk1","stv1");       console.log(testId.stk1);//undefined       console.log(testId.getAttribute("stk1"));//selfValue       testId.stk1 = "stv2";       console.log(testId.stk1);//stv2       console.log(testId.getAttribute("stk1"));//stv1

此外,对于值是true/false的property,类似于input的checked attribute等,attribute取得值是HTML文档字面量值,property是取得计算结果,property改变并不影响attribute字面量,但attribute改变会影响property计算

<input type="checkbox" id="checkboxId"/>
var checkbox1 =  document.getElementById("checkboxId");                alert(checkbox1.checked);//false         alert(checkbox1.getAttribute("checked"));//null        /* checkbox1.setAttribute("checked","checked");         alert(checkbox1.checked);//true, setAttribute影响property*/         checkbox1.checked = true;         alert(checkbox1.getAttribute("checked"));//null property不影响setAttribute影响property

对于一些和路径相关的属性,两者取得值也不尽相同,但是同样attribute取得是字面量,property取得是计算后的完整路径

<a id="testA" href="#">Click</a>

 

 var testA =  document.getElementById("testA"); console.log(testA.href);//file:///C:/Users/%E6%B0%B8%E4%BF%A1/Desktop/static/main.html#  console.log(testA.getAttribute("href"));//#

看完上面的文章,看一下主题,attr() 和 prop()

首先贴源码:

attr:

attr: function( elem, name, value, pass ) {    var ret, hooks, notxml,        nType = elem.nodeType;    // don‘t get/set attributes on text, comment and attribute nodes    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {        return;    }    if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {        return jQuery( elem )[ name ]( value );    }    // Fallback to prop when attributes are not supported    if ( typeof elem.getAttribute === "undefined" ) {        return jQuery.prop( elem, name, value );    }    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );    // All attributes are lowercase    // Grab necessary hook if one is defined    if ( notxml ) {        name = name.toLowerCase();        hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );    }    if ( value !== undefined ) {        if ( value =http://www.mamicode.com/== null ) {            jQuery.removeAttr( elem, name );            return;        } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {            return ret;        } else {            elem.setAttribute( name, value + "" );            return value;        }    } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {        return ret;    } else {        ret = elem.getAttribute( name );        // Non-existent attributes return null, we normalize to undefined        return ret === null ?            undefined :            ret;    }}

prop:

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 ) {        if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {            return ret;        } else {            return ( elem[ name ] = value );        }    } else {        if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {            return ret;        } else {            return elem[ name ];        }    }}

attr方法里面,最关键的两行代码,elem.setAttribute( name, value + “” )和ret = elem.getAttribute( name ),很明显的看出来,使用的DOM的API setAttribute和getAttribute方法操作的属性元素节点
而prop方法里面,最关键的两行代码,return ( elem[ name ] = value )和return elem[ name ],你可以理解成这样document.getElementById(el)[name] = value,这是转化成JS对象的一个属性。

再来看例子:

<input type="checkbox" id="test" abc="111" />
$(function(){    el = $("#test");    console.log(el.attr("style"));  //undefined    console.log(el.prop("style"));  //CSSStyleDeclaration对象    console.log(document.getElementById("test").style); //CSSStyleDeclaration对象});
  1. el.attr(“style”)输出undefined,因为attr是获取的这个对象属性节点的值,很显然此时没有这个属性节点,自然输出undefined
  2. el.prop(“style”)输出CSSStyleDeclaration对象,对于一个DOM对象,是具有原生的style对象属性的,所以输出了style对象
  3. 至于document.getElementById(“test”).style和上面那条一样
el.attr("abc","111")console.log(el.attr("abc")); //111console.log(el.prop("abc")); //undefined

首先用attr方法给这个对象添加abc节点属性,值为111,可以看到html的结构也变了

  1. el.attr(“abc”)输出结果为111,再正常不过了
  2. el.prop(“abc”)输出undefined,因为abc是在这个的属性节点中,所以通过prop是取不到的

我们再接着来:

el.prop("abc", "222");console.log(el.attr("abc")); //111console.log(el.prop("abc")); //222

我们再用prop方法给这个对象设置了abc属性,值为222,可以看到html的结构是没有变化的。输出的结果就不解释了。

提一下,在遇到要获取或设置checked,selected,readonly和disabled等属性时,用prop方法显然更好,比如像下面这样:

<input type="checkbox" id="test" checked="checked" />
console.log(el.attr("checked")); //checkedconsole.log(el.prop("checked")); //trueconsole.log(el.attr("disabled")); //undefinedconsole.log(el.prop("disabled"));  //false

关于checked 属性需要记住的最重要的一点是:它和checked property并不是一致的。实际上这个attribute和defaultChecked property一致,而且只应该用来设置checkbox的初始值。checked attribute并不随着checkedbox的状态而改变,但是checked property却跟着变。因此浏览器兼容的判断checkebox是否被选中应该使用property