特性和属性是javascript中两个很重要同时也很容易混淆的概念:
特性(attribute) 是DOM构建的一个组成部分
属性(property) 是元素保持运行时信息的主要手段,并且可以通过属性获取这些运行时信息
我们可以通过一个简单的示例来演示特性与属性的区别:
<html>
<head></head>
<body>
<a id="link" href="./1.html">link</a>
<script>
var link = document.getElementById('link');
var newHref = "./2.html";
link.href = newHref;
console.log(link.href===newHref,link.getAttribute('href')===newHref);
// false true
console.log("attribute:",link.getAttribute('href'));
// attribute: ./2.html
console.log("property:",link.href);
// property: file:///C:/Users/Administrator/Desktop/jquery/2.html
</script>
</body>
</html>
在上述代码中,我们创建了一个链接标签,获取它的引用,并将它的href属性修改为一个新值。通过console日志我们可以看到不管是特性还是属性都已经被成功修改,但是特性与属性的值却是不一样的。特性的值是我们赋予的,而属性值则由相对路径被转成了绝对路径。回忆前文关于属性的定义,属性是元素保持运行时信息的主要手段。这里已经能看出属性和特性的一个区别。
1 DOM 特性和 DOM 属性
在访问或设置元素的特性值时有两种方法:使用传统的 DOM 方法 getAttribute
和 setAttribute
或使用 DOM 对象上与之对应的属性。
举例来说,假设一个元素保存在对象e中,如果我们希望获取元素的id属性,可以通过下面两种方式:
e.getAttribute('id');
e.id;
同样,不管我们通过下面的哪种方式,都可以修改元素的id值:
e.setAttribute('id','test');
e.id = 'test';
也就是说,设置了特性的值,属性值会跟着改变,反之亦然。
但是这并不代表特性和属性共享一个相同的值。特性和对应的属性虽然有联系,但并不总是相同的。
1.1 跨浏览器命名
在谈到特性和对应的属性命名时,属性的名称在不同的浏览器上通常更加一致。如果在一个浏览器中,我们能通过一个特定的名称访问一个属性,那么在其他浏览器中,很有可能也可以用同样的名称访问该属性。属性命名之间虽然会有一些差异,但是特性和属性命名之间的差异则会更多。
举个例子,在大多数浏览器中都可以用class获取到元素的class特性,但是IE中却需要使用className,并且IE对应的属性名称也是className。
1.2 命名限制
特性(attribute),表示为传递给 DOM 方法的字符串,其命名规范是非常自由的。但属性名称,由于可以作为标识符使用点表示法进行访问,所以其命名规范是更受限的,属性名称必须符合标识符的规则,而且还有一些保留关键字也不能用。
例如,<label>
的for特性是关键字,所以在ECMAScript规范中,可以用htmlFor属性进行表示,同样的class由className来表示。另外,由多个单词组成的特性名称由“驼峰式”的属性名称来表示,例如,特性readonly的属性名称为readOnly。更多差异,参见下表。
特性名称(Attribute) | 属性名称 (Property) |
---|---|
for | htmlFor |
class | className |
readonly | readOnly |
maxlength | className |
class | className |
cellspacing | cellSpacing |
rowspan | rowSpan |
colspan | colSpan |
tabindex | tabIndex |
cellpadding | cellPadding |
usemap | useMap |
frameborder | frameBorder |
contenteditable | contentEditable |
1.3 自定义特性的行为
并不是所有的特性都有元素的属性来表示。虽然这适用于 HTML DOM 的原生特性,但我们在页面元素上定义的自定义特性(custom attributes),则不会自动转换为元素属性的表达方式。要想访问这些元素的特性值,需要使用 DOM 方法 getAttribute() 和 setAttribute() 。
如果不确定一个特性的属性是否存在,可以对其进行测试,如果不存在的话再使用 DOM 方面,参见下面的例子:
let value = element.someValue?element.someValue:
element.getAttribute('someValue');
在 HTML5 中,对所有的自定义特性使用 data-
前缀,以便遵守 HTML5 的规范。即便使用的是 HTML4,也同样建议使用这种方式,以便将标签适应未来。除此之外,这是分离自定义特性和原生特性一个很好的约定。
1.4 性能注意事项
总的来说,属性的访问速度比相应的特性访问速度要快,特别是在IE浏览器中。让我们来证明一下。
<html>
<head></head>
<body>
<div id="test"></div>
<script>
var count = 5000000;
var element = document.getElementById('test');
var begin ,end, value,n;
begin = new Date();
for(n=0;n<count;n++){
value = element.getAttribute('id');
}
end = new Date();
console.log("getAttribue cost "+(end.getTime()-begin.getTime())+" ms.");
begin = new Date();
for(n=0;n<count;n++){
value = element.id;
}
end = new Date();
console.log("element.id cost "+(end.getTime()-begin.getTime())+" ms.");
begin = new Date();
for(n=0;n<count;n++){
element.setAttribute('id','test');
}
end = new Date();
console.log("setAttribue cost "+(end.getTime()-begin.getTime())+" ms.");
begin = new Date();
for(n=0;n<count;n++){
element.id = 'test';
}
end = new Date();
console.log("write element.id cost "+(end.getTime()-begin.getTime())+" ms.");
</script>
</body>
</html>
下面是不同浏览器的运行结果(单位为ms):
浏览器 | getAttribute | Property获取 | setAttribue | Property设置 |
---|---|---|---|---|
chrome 71.0.3578.98 | 288 | 96 | 1715 | 669 |
Firefox 64.0 | 13 | 12 | 746 | 439 |
Edge 17.17134 | 3018 | 2301 | 48125 | 1691 |
IE 11 | 33676 | 16386 | 40683 | 15775 |
可以看出来通过属性设置或者获取值的性能明显优于通过 DOM 方式, IE在这方面的表现尤为突出。