在微博上看到有人讨论 attributes 与 properties,甚至 arguments 与 parameters 的区别。正好在看到一篇好的网文,翻译共享。
文本来自:Attributes and custom properties ——By llya Kantor
Attributes and custom properties(特性与自定义属性)
- Properties
- Attributes
- Properties 和 Attributes 的同步
- id
- href
- value
- class/className
- 旧式IE浏览器的乐趣
- 作为DOM节点的 Attributes
- 总结
一个 DOM 节点包含 attributes 和 properties。因为彼此间相互关联,人们常常将他们混合在一起,但他们确是两种不同的东西。
Properties(属性)
DOM 节点是一个对象,因此可以像 Javascript 中的对象一样存储自定义的 properties 和 methods。
例子展示了如何给 document.body 添加 properties 和 methods:
document.body.myData = { name: 'John' }; alert(document.body.myData.name); // John document.body.sayHi = function() { alert(this.nodeName); }; document.body.sayHi(); // BODY
自定义的 properties 和 methods 仅在 Javascript 中可见,并影响 HTML 。
同时,自定义 properties 与本地 properties 可用 for..in 循环展示出来:
document.body.custom = 5; var list = []; for(var key in document.body) { list.push(key); } alert(list.join('\n'));
自定义的 DOM properties:
- 可为任何类型的值,属性名对大小写敏感。
- 不影响HTML。
Attribute(特性)
DOM 节点提供如下访问 HTML attributes的标准方法:
elem.hasAttribute(name) - 检查attribute是否存在
elem.getAttribute(name) - 获得attribute的值
elem.setAttribute(name, value) - 设置attribute
elem.removeAttribute(name) - 删除attribute
IE 8的兼容模式及其以下版本破坏了attributes,这些版本:
- 只提供了 getAttribute 和 setAttribute 方法
- 实际修改的是 DOM propertied,而不是 attributes
- IE 8以下版本中合并了 Attribute 与 property。虽然有时会导致奇怪的结果,但是我们在这讨论的对 attributes 操作方法仍然有效。
对比 properties,attributes 具有:
- 可能只能是字符串(有些会用 toString() 转换)
- 名称对大小写不敏感,因为 HTML attributes是不敏感的
- 可在 innerHTML 中显示(除非是老的 IE 版本)
- 可以用元素属性 .attributes 来列出所有的 attribute,它是个类数组。
如下例子中展示了一个 HTML 结构,并对其设置 attributes:
<body> <div about="Elephant" class="smiling"></div> <script> var div = document.body.children[0] alert( div.getAttribute('ABOUT') ) // (1) div.setAttribute('Test', 123) // (2) alert( document.body.innerHTML ) // (3) </script> </body>
当运行上述代码,注意数字标注的几点:
- 虽然 getAttribute('ABOUT‘) 用了大写名称,但并不影响结果。
- 可以对 attribute 设置字符串或其他基本类型,这些都将会自动转换成字符串。对象本应当自动转换,但在 IE 中存在问题,因此请坚持使用基本类型。
- innerHTML 中包括了新的 ‘test' attribute。
Properties 与 Attributes 的同步
每种 DOM 节点都有标准的属性。
例如,‘A’标签:Interface HTMLAnchorElement。它包含有 'href' 、'accessKey' 以及其他特定的属性。除此之外,它还从 HTMLElement 那继承了 'id' 即其他attribute。
标准的 DOM properties 与 attributes 是同步的。
id
例如,浏览器中 'id‘ attribute与 'id' property是同步的。
<script> document.body.setAttribute('id','la-la-la'); alert(document.body.id); // la-la-la </script>
href
但同步不能保证值也相同。来看看例子中的 ‘href' 属性:
<a href="#"></a> <script> var a = document.body.children[0]; a.href = '/' alert( 'attribute:' + a.getAttribute('href') ); // '/' alert( 'property:' + a.href ); // IE: '/', others: full URL </script>
这是因为根据 W3C specification,href 必须是一个 ‘well-formed’(良好形式)的链接。
还有一些属性,它们是同步的,但对于 properties 和 attributes 却不是相同的副本。例如 input.checked :
<input type="checkbox" checked> <script> var input = document.body.children[0]; alert( input.checked ); // true alert( input.getAttribute('checked') ); // empty string </script>
input.checked 的 property 只能是 true 或者 false,但 attribute可以是任何输入的值。
value
同样存在一些 'one-way‘(单向)同步的内置属性。
例如,input.value 从 attribute 中同步(即 property 从 attribute 中获得同步):
<body> <input type="text" value="markup"> <script> var input = document.body.children[0]; input.setAttribute('value', 'new'); alert( input.value ); // 'new', input.value changed </script> </body>
但是 attribute 不能从 property 中获得同步:
<body> <input type="text" value="markup"> <script> var input = document.body.children[0]; input.value = 'new'; alert(input.getAttribute('value')); // 'markup', not changed! </script> </body>
例如当访问者输入了某些字符后,'value' attribute 在 property 更新后维持了原始值。原始值可以用来检验 input 是否变化,或者重置它。
class/className
名称的特例:attribute 中的 ‘class’ 对应 property中的 ‘className’
因为 ‘class' 是 Javascript 的保留字,attribute的 ‘class’ 对应的 property 名称是 ‘className’:
<body> <script> document.body.setAttribute('class', 'big red bloom'); alert( document.body.className ); // ^^^ </script> </body>
注意,上述例子在 IE 9以下的版本不可用,因为怪异模式混合了 properties 和 attributes。
只要始终使用 className property而不是 class attribute,就可以避免浏览器差异带来的麻烦。
旧式IE浏览器的乐趣
首先,IE 9以下的版本同步了所有的 property 和 attribute。
document.body.setAttribute('try-in-ie', 123) alert( document.body['try-in-ie'] === 123 ) // true in IE<9
注意到这里的变量(123)类型是一样的,attribute 并没有将其转换成字符串(实际上它应该转换)。
其次,在 IE 8以下版本(以及IE 8的IE 7兼容模式)的 properties和 attributes 是相同的。因此产生了一些有趣的结果。
例如,properties 的名称是大小写敏感的,attributes 却不是。如果浏览器认为它们是相同的,那下面的结果将会是什么样的呢?
document.body.abba = 1 // assign property (now can read it by getAttribute) document.body.ABBA = 5 // assign property with another case // must get a property named 'ABba' in case-insensitive way. alert( document.body.getAttribute('ABba') ) // ??
浏览器为了避免这种情况,将获得名称的首字母作为默认的大写小形式。IE 还为 getAttribute 提供了一个可选的参数,用来指定大小写形式。详见 MSDN getAttribute。
除了IE 9以下版本的浏览器都可用 ‘class’ attribute 来改变 class 的值。但尽量不要使用它,始终用‘className’ property 代替。
要想在IE中生存,必须确保正确地使用 attributes。
或者,始终使用 properties,除非你确实需要用attributes。
确实需要用 attributes 的情况包括:
- 获得自定义的HTML attribute,因为它并不同步到DOM的 property 中。
- 获得标准HTML attributes 的原始值,例如<INPUT value = '...'>。
作为DOM 节点的 attributes
可以通过 elem.attributes 集合访问元素的 attributes。
在 attributes 集合中,所有 attribute 都可以表示成一个特殊的DOM节点。它包括名称、值和其他属性。
例如:
<span style="color:blue" id="my">text</span> <script> var span = document.body.children[0]; alert( span.attributes['style'].value ); // "color:blue;" alert( span.attributes['id'].value ); // "my" </script>
另外,在IE 8的兼容模式及其以下版本在处理 ‘style’ 属性时会变得疯狂……猜猜为啥?
Attribute DOM节点不是文档树的一部分,仅仅可从元素来访问它们。
总结
Attributes 和 properties 都是DOM模型的核心特征。
他们的异同表:
property | attribute |
任意值 | 字符串 |
名称对大小写敏感 | 不敏感 |
innerHTML中不显示 | 显示 |
标准的 DOM properties 和 attributes 是同步的,自定义的不是 | |
IE 8兼容模式及其以下版本将 attributes 与 properties 混合起来,并且弄得很糟糕 |
如果需要在 HTML中自定义 attributes,可以在HTML5中使用 ‘date-* attribute‘ 的形式。详见HTML5标准中的Custom data attributes部分。
在实际应用中,98%的 DOM 操作都是使用 properties。
只有两种情形需要使用attributes:
- 自定义 HTMLattributes,因为它并不同步到DOM property。
- 访问内置的 HTML attributes,这些 attribute 不能从 property 同步过来。例如 INPUT标签的value值。