首页 > 技术文章 > JQuery1.11版本对prop和attr接口的含义分离导致问题分析

lightsong 2016-04-28 00:17 原文

问题背景

实验中, 在jquery1.7版本, attr("value")  和 val() 接口获取 input 控件的值, 都是一致的, 都是当前控件值。

但是 jquery1.11版本,已经将 这两个接口的返回值分离,  attr("value") 获取的是 控件的初始值(default value),

只有val()属性才能获取到 控件当前值, 例如 用户修改 了输入的值, 必须使用val()获取最新值。

 

对于 checkbox 和 radio 等控件,  不能使用 val()接口获取,当前值, 需要使用prop("checked")来判断间接判断当前选中控件的值。

经过分析 对于input控件, val接口的实现, 也是调用prop接口来获取 控件的当前值, 即 prop("value").

 

但是为什么要拆分出 attr 和 property 概念?

 

查阅MDN相关资料

https://developer.mozilla.org/zh-CN/docs/Web/API/Element/setAttribute

使用 setAttribute() 修改某些属性值时,可能得不到期望结果,尤其是 XUL 中的 value,这是由于 attribute 指定的是默认值。要访问或修改当前值,应该使用 property 属性。例如,使用 elt.value 代替 elt.setAttribute('value', val)

对于属性的改变,或者不改变(保持默认值情况),其当前值并不跟控件的attr属性直接相关。

如果要获取当前属性值, 需要使用属性 dom的 property 来获取 控件。

 

https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute

Using setAttribute() to modify certain attributes, most notably value in XUL, works inconsistently, as the attribute specifies the default value. To access or modify the current values, you should use the properties. For example, use elt.value instead of elt.setAttribute('value', val).

 

只解释了 attr 能够设置和获取默认值。

但是property获取的是当前的值。

 

还是没有解释出根本原因, 为什么要这么设计?

 

那什么是 attr 什么是 property呢?

 

什么是attribute?

DOM 的 atrributes

https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes

  The Element.attributes property returns a live collection of all attribute nodes registered to the specified node. It is a NamedNodeMap, not an Array, so it has no Array methods and the Attr nodes' indexes may differ among browsers. To be more specific, attributes is a key/value pair of strings that represents any information regarding that attribute.

 

什么是attribute?

attribute 对象表达 attribute属性。

https://developer.mozilla.org/en-US/docs/Web/API/Attr

This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types.

 

Properties

isId
Indicates whether the attribute is an "ID attribute". An "ID attribute" being an attribute which value is expected to be unique across a DOM Document. In HTML DOM, "id" is the only ID attribute, but XML documents could define others. Whether or not an attribute is unique is often determined by a DTD or other schema description.
name
The attribute's name.
ownerElement Deprecated since Gecko 7.0 Obsolete since Gecko 29.0
This property has been removed from Firefox 29. Since you can only get Attr objects from elements, you should already know the owner.
Contrary to above claim, Document.evaluate can return Attr objects from an XPath, in which case you would not easily know the owner.
schemaTypeInfo
?
specified
This property always returns true. Originally, it returned true if the attribute was explicitly specified in the source code or by a script, and false if its value came from the default one defined in the document's DTD.
value
The attribute's value.

 

以上都是 DOM 结点的 attr 表达形式介绍。

实际上attribute是 html 的属性, 是html的专有名词。

https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Introduction#Attributes

Attributes

The start tag may contain additional information, as in the preceding example. Such information is called an attribute. Attributes usually consist of 2 parts:

  • An attribute name
  • An attribute value

 

属性表达了 HTML 中 标签的 属性(属性值为字符串), 映射到DOM上表示为 ATTR 对象。

属性 表达的是文档层面的 表达, 为文档加载后的初始状态 和 被修改(例如js 调用 setAttribute接口)的行为,  改变的是文档初始的结构, 并不修改 文档的  当前状态。

即, 通过setAttribute 修改了 value,并不影响 控件当前的value值。 下面介绍property的时候验证。

 

什么是property?

面向对象的思想, 对象中的所有数据成员都是 property。 见下网址描述:

http://coursesweb.net/actionscript/oop-object-oriented-programming

Object-Oriented programming (OOP) is a specific programming technique to create chunks of programming code (called "objects") that do a specific job, and divide the script into distinct pieces that can be easily managed. These pieces of code can be grouped to form another object.
- For example, the wheels, frame, the pedal, and the handlebars of a bike are separate objects, but if you unite them, they can form a bicycle, which also can be considered an object.
The same is in ActionScript programming, the text, line, shape, movie clip symbol, functions, etc., anything is considered an object.

• Objects are defined using two primary identifiers (or members): properties and methods.

    • Properties - are characteristics that define an object.
      - For example, "color" can be property of a DVD player.
    • Methods - are actions that can be performed by an object.
      For example, Play, and Pause are methods of a DVD player.

 

对于js中, 使用dom接口获取的一个dom对象后, dom对象有若干暴露于js语言中的 属性, 这些属性, 可能跟 html 标签中的 属性名重复,

例如:

<input id="xx"  name="xx" value="bbb">

document.getElementById("xx").value // bbb

document.getElementById("xx").value = "aaa" // aaa

document.getElementById("xx").getAttribute("value")    //bbb

 

<!DOCTYPE html>

<html>

 <head>
  <title>Attributes example</title>

 </head>

<body>
<input id="xx"  name="xx" value="bbb">

  <script type="text/javascript">
alert(document.getElementById("xx").value) // bbb

document.getElementById("xx").value = "aaa" // aaa

alert(document.getElementById("xx").value) // aaa

alert(document.getElementById("xx").getAttribute("value"))    //bbb
  </script>

</body>
</html>

 

 

也有可能不重复, 例如select控件, 没有获取当前值得的value attribute, 但是有value property可以获取当前值:

<select id="xx">

<option value="sss"  selected="selected">sss</option>

<option value="bbb">bbb</option>

</select>

document.getElementById("xx").value // sss

document.getElementById("xx").value = "bbb" // bbb

document.getElementById("xx").getAttribute("value")    //null

 

<!DOCTYPE html>

<html>

 <head>
  <title>Attributes example</title>

 </head>

<body>
<select id="xx">

<option value="sss"  selected="selected">sss</option>

<option value="bbb">bbb</option>

</select>


  <script type="text/javascript">
alert(document.getElementById("xx").value) // sss

document.getElementById("xx").value = "bbb" 

alert(document.getElementById("xx").value) // bbb

alert(document.getElementById("xx").getAttribute("value"))    //null
  </script>

</body>
</html>

 

基于上面的代码, 进一步验证, 设置了attbute, 不会改变 property的值。

<!DOCTYPE html>
<html>
 <head>
  <title>Attributes example</title>
 </head>
<body>
<input id="xx" name="xx" value="bbb">
  <script type="text/javascript">
alert(document.getElementById("xx").value) // bbb
document.getElementById("xx").value = "aaa" // aaa
alert(document.getElementById("xx").value) // aaa
alert(document.getElementById("xx").getAttribute("value")) //bbb
document.getElementById("xx").setAttribute("value", "ccc");
alert(document.getElementById("xx").value) // aaa
alert(document.getElementById("xx").getAttribute("value")) //ccc
  </script>
</body>
</html>

 

不仅仅value不一致, a标签的href属性也是不一致的:

http://stackoverflow.com/questions/5874652/prop-vs-attr

var link = document.getElementById('fooAnchor');
alert(link.href);                 // alerts "http://example.com/foo.html"
alert(link.getAttribute("href")); // alerts "foo.html"

 

 

JQuery官网的解释

http://api.jquery.com/prop/

Attributes vs. Properties

The difference between attributes and properties can be important in specific situations. Before jQuery 1.6, the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior. As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() retrieves attributes.

For example, selectedIndex, tagName, nodeName, nodeType, ownerDocument, defaultChecked, and defaultSelected should be retrieved and set with the .prop() method. Prior to jQuery 1.6, these properties were retrievable with the .attr() method, but this was not within the scope of attr. These do not have corresponding attributes and are only properties.

 在1.6版本之前, attr接口 是考虑获取属性值的 property value, 但是这样导致  非一致的行为

例如 如上面试验结论:

select.value 有, 但是 select.getAttribute("value") 就没有, 如果给 html select标签设置了 value 属性, 则 select.getAttribute("value") 获取的是html标签属性值, select.value 获取的是 控件当前值。

 

1.6版本之后, 提供了 prop 接口 获取属性值。 这样 getAtrribute 则专职负责获取属性。

 

W3HELP给出历史变革

http://w3help.org/zh-cn/causes/SD9006

SD9006: IE 混淆了 DOM 对象属性(property)及 HTML 标签属性(attribute),造成了对 setAttribute、getAttribute 的不正确实现

 

标准参考

根据 DOM (Core) Level 1 规范中的描述,getAttribute 与 setAttribute 为 Element 接口下的方法,其功能分别是通过 "name" 获取和设置一个元素的属性(attribute)值。getAttribute 方法会以字符串的形式返回属性值,若该属性没有设定值或缺省值则返回空字符串。setAttribute 方法则无返回值。
在 DOM Level 2 规范中,则更加明确了 getAttribute 与 setAttribute 方法参数中的 "name" 的类型为 DOMString,setAttribute 方法参数中的 "value" 的类型为 DOMString,getAttribute 的返回值类型也为 DOMString。

DOMString  getAttribute(in DOMString name);
void    setAttribute(in DOMString name, in DOMString value) raises(DOMException);

HTML 文档中的 DOM 对象的属性(property)被定义在 DOM (HTML) 规范中。这个规范中明确定义了 document 对象以及所有标准的 HTML 元素所对应的 DOM 对象拥有的属性及方法。
因为在早期的 DOM Level 0 阶段,某些 HTML 标签的属性会将其值暴露给对应的 DOM 对象的属性,如 HTML 元素的 id 属性与其对应的 DOM 对象的 id 属性会保持一种同步关系,然而这种方式目前已经废弃,这是由于它不能被扩展到所有可能存在的 XML 的属性名称。W3C 建议使用 DOM (Core) 中 Element 接口上定义的通用方法去获取(getting)、设置(setting)及删除(removing)元素的属性。

 

举例来说,在一个 HTML 文档中存在一个 SPAN 元素,根据 DOM (HTML) 规范,SPAN 元素在页面中生成一个相对应的对象,这个对象派生自 HTMLElement 接口,而 HTMLElement 接口则继承自 Element 接口。HTMLElement 接口中包含 id 属性。我们可以通过 HTMLElement 接口中的 id 属性获得这个元素的标识符,而通过 Element 接口中的 getAttibute 方法传入参数 "id" 同样可以获得标识符。


虽然这两种方式可以获得相同的值,但是却有着天壤之别。

从规范层面上看,getAttribute 方法所属的 DOM (Core) 规范定义了访问和操作文档对象的一套对象和接口,这其中包含了对 HTML 及 XML 的解析及操作;

HTMLElement 接口中 id 属性所属的 DOM (HTML) 规范为 DOM (Core) 的扩展,描述了 HTML 及 XHTML 的对象的细节。

 

而从 HTML 文档代码的层面上看,一个元素的标记中的 HTML 属性(attribute)与其对应的 DOM 对象的属性(property)也是完全不同的两个概念。

 

关于 getAttribute 与 setAttribute 的详细信息,请参考 DOM (Core) Level 1Level 2 中的内容。

关于 HTML DOM 对象 的详细信息,请参考 DOM (HTML) Document Object Model HTML,特别是 1.6.1. Property Attributes 中的内容。

只说 dom property不是通用的, 所以使用了更通用的 setatrribute。 根本原因可看不出来,也是语焉不详。

 

property描述:

https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-642250288

 

atrribute定义:

https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-637646024

 

知乎上找到的答案

 

https://www.zhihu.com/question/38510295/answer/76751073

那么 attributeproperty 有啥区别呢?

  1. attribute 是 HTML 里写好的,property 是 DOM 节点的属性;
  2. 有一些 attribute 会映射到 property (如 value/id 等),有一些 attribute 则不会;
  3. HTML 里写好的 attribute,在映射到 property 时作为 property 的初始值;
  4. 后续的修改最好是直接改 property,改 attribute 不一定生效,关于这一点,每一个浏览器、每一个元素都可能会有自己的行为,data attribute/property 的引入,使得这个问题更复杂了。

 

DOM SPECIFICATION定义

https://www.w3.org/TR/REC-DOM-Level-1/

dom level 1

The goal of the DOM specification is to define a programmatic interface for XML and HTML. The DOM Level 1 specification is separated into two parts: Core and HTML. The Core DOM Level 1 section provides a low-level set of fundamental interfaces that can represent any structured document, as well as defining extended interfaces for representing an XML document. These extended XML interfaces need not be implemented by a DOM implementation that only provides access to HTML documents; all of the fundamental interfaces in the Core section must be implemented. A compliant DOM implementation that implements the extended XML interfaces is required to also implement the fundamental Core interfaces, but not the HTML interfaces. The HTML Level 1 section provides additional, higher-level interfaces that are used with the fundamental interfaces defined in the Core Level 1 section to provide a more convenient view of an HTML document. A compliant implementation of the HTML DOM implements all of the fundamental Core interfaces as well as the HTML interfaces.

 

从上面可以看到,

The DOM Level 1 specification is separated into two parts: Core and HTML.

 

Core -- 负责文档结构。

HTML -- 负责 HTML 文档的便利使用。

 

DOM core attr 接口:

https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-745549614

IDL Definition
interface Element : Node {
  readonly attribute  DOMString            tagName;
  DOMString                 getAttribute(in DOMString name);
  void                      setAttribute(in DOMString name, 
                                         in DOMString value)
                                         raises(DOMException);
  void                      removeAttribute(in DOMString name)
                                            raises(DOMException);
  Attr                      getAttributeNode(in DOMString name);
  Attr                      setAttributeNode(in Attr newAttr)
                                             raises(DOMException);
  Attr                      removeAttributeNode(in Attr oldAttr)
                                                raises(DOMException);
  NodeList                  getElementsByTagName(in DOMString name);
  void                      normalize();
};

这样我们豁然开朗, attr 也只对文档的结构负责。

 

 影响:

http://www.tuicool.com/articles/6NVvaeE

property与attribute都是dom的核心功能,鉴于有的属性上会出现单项同步的情况似乎attribute貌似更保险一点,以下是二者的关系表:

 

作用:

可以使用attr接口构造dom节点, 这个层面上, 仅仅是文档结构(此时遵守 DOM CORE), 其负责文档的初始形式;

DOM HTML的接口也被添加到DOM节点上(不管DOM有没有加入文档流中), property属性都可以调用,改变动态改变的状态。

 

下面给出例子证明, attr是文档初始值, property是动态改变的值。

首先构造了一个input text控件, 控件value attr最终被设置为  ccc

将此控件插入文档, 插入文档的位置为  form 表单, 其中包括一个 reset 按钮。 

页面显示后, 点击reset按钮, input输入框的值变为 ccc。

<!DOCTYPE html>

<html>

 <head>
  <title>Attributes example</title>

 </head>

<body>
    <form>

    <input type="reset" name="reset" value="reset">
    </form>

  <script type="text/javascript">
var input = document.createElement("input");
input.setAttribute("type", "text");
input.setAttribute("name", "q");
input.setAttribute("value", "bbb");
//input.setAttribute("class",bordercss);

input.value = "aaa"

document.forms[0].appendChild(input)

input.setAttribute("value", "ccc");

  </script>

</body>
</html>

 

显示:

 

点击reset后

 

推荐阅读