这些答案并没有真正解决 properties 和 attributes 之间的巨大混淆。此外,根据 Javascript 原型,有时您可以使用元素的属性来访问属性,有时则不能。
首先,您必须记住 HTMLElement 是一个 Javascript 对象。像所有对象一样,它们具有属性。当然,您可以在HTMLElement 中创建一个几乎任何您想要的属性,但它不必与 DOM(页面上的内容)做任何事情。点符号 (.) 用于 properties。现在,有一些特殊的 properties 映射到属性,在当时或写作时,只有 4 个是保证的(稍后会详细介绍)。
所有HTMLElements 都包含一个名为attributes 的属性。 HTMLElement.attributes 是一个 live NamedNodeMap 与 DOM 中的元素相关的对象。 “实时”意味着当 DOM 中的节点发生变化时,它们会在 JavaScript 端发生变化,反之亦然。在这种情况下,DOM 属性是有问题的节点。 Node 具有您可以更改的 .nodeValue 属性。 NamedNodeMap 对象有一个名为 setNamedItem 的函数,您可以在其中更改整个节点。您也可以通过密钥直接访问节点。比如你可以说.attributes["dir"]和.attributes.getNamedItem('dir');是一样的(旁注,NamedNodeMap不区分大小写,所以你也可以传'DIR');
在HTMLElement 中直接有一个类似的功能,您只需调用setAttribute,它会自动创建一个节点,如果它不存在并设置nodeValue。还有一些属性可以作为HTMLElement中的属性通过特殊属性直接访问,例如dir。以下是其外观的粗略映射:
HTMLElement {
attributes: {
setNamedItem: function(attr, newAttr) {
this[attr] = newAttr;
},
getNamedItem: function(attr) {
return this[attr];
},
myAttribute1: {
nodeName: 'myAttribute1',
nodeValue: 'myNodeValue1'
},
myAttribute2: {
nodeName: 'myAttribute2',
nodeValue: 'myNodeValue2'
},
}
setAttribute: function(attr, value) {
let item = this.attributes.getNamedItem(attr);
if (!item) {
item = document.createAttribute(attr);
this.attributes.setNamedItem(attr, item);
}
item.nodeValue = value;
},
getAttribute: function(attr) {
return this.attributes[attr] && this.attributes[attr].nodeValue;
},
dir: // Special map to attributes.dir.nodeValue || ''
id: // Special map to attributes.id.nodeValue || ''
className: // Special map to attributes.class.nodeValue || ''
lang: // Special map to attributes.lang.nodeValue || ''
}
因此您可以通过 6 种方式更改 dir 属性:
// 1. Replace the node with setNamedItem
const newAttribute = document.createAttribute('dir');
newAttribute.nodeValue = 'rtl';
element.attributes.setNamedItem(newAttribute);
// 2. Replace the node by property name;
const newAttribute2 = document.createAttribute('dir');
newAttribute2.nodeValue = 'rtl';
element.attributes['dir'] = newAttribute2;
// OR
element.attributes.dir = newAttribute2;
// 3. Access node with getNamedItem and update nodeValue
// Attribute must already exist!!!
element.attributes.getNamedItem('dir').nodeValue = 'rtl';
// 4. Access node by property update nodeValue
// Attribute must already exist!!!
element.attributes['dir'].nodeValue = 'rtl';
// OR
element.attributes.dir.nodeValue = 'rtl';
// 5. use setAttribute()
element.setAttribute('dir', 'rtl');
// 6. use the UNIQUELY SPECIAL dir property
element["dir"] = 'rtl';
element.dir = 'rtl';
您可以使用方法 #1-5 更新所有属性,但只能使用方法 #6 更新 dir、id、lang 和 className。
HTMLElement 的扩展
HTMLElement 具有这 4 个特殊属性。一些元素是HTMLElement 的扩展类,具有更多的映射属性。例如,HTMLAnchorElement 具有 HTMLAnchorElement.href、HTMLAnchorElement.rel 和 HTMLAnchorElement.target。但是,注意,如果您在没有这些特殊属性的元素上设置这些属性(例如在 HTMLTableElement 上),那么属性不会更改,它们只是普通的自定义属性。为了更好地理解,下面是它的继承示例:
HTMLAnchorElement extends HTMLElement {
// inherits all of HTMLElement
href: // Special map to attributes.href.nodeValue || ''
target: // Special map to attributes.target.nodeValue || ''
rel: // Special map to attributes.ref.nodeValue || ''
}
自定义属性
现在大警告:像所有 Javascript 对象一样,您可以添加自定义属性。但是,这些不会改变 DOM 上的任何内容。你可以这样做:
const newElement = document.createElement('div');
// THIS WILL NOT CHANGE THE ATTRIBUTE
newElement.display = 'block';
但那是一样的
newElement.myCustomDisplayAttribute = 'block';
这意味着添加自定义属性不会链接到.attributes[attr].nodeValue。
性能
我已经构建了一个 jsperf 测试用例来显示差异:https://jsperf.com/set-attribute-comparison。基本上,按顺序:
- 自定义属性,因为它们不影响 DOM 并且不是属性。
- 浏览器提供的特殊映射(
dir、id、className)。
-
如果属性已经存在,
element.attributes.ATTRIBUTENAME.nodeValue =
- setAttribute();
-
如果属性已经存在,
element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
element.attributes.ATTRIBUTENAME = newNode
element.attributes.setNamedItem(ATTRIBUTENAME) = newNode
结论(TL;DR)
-
使用来自HTMLElement 的特殊属性映射:element.dir、element.id、element.className 或 element.lang。
-
如果您 100% 确定该元素是具有特殊属性的扩展 HTMLElement,请使用该特殊映射。 (您可以通过if (element instanceof HTMLAnchorElement)查看)。
-
如果您 100% 确定该属性已存在,请使用 element.attributes.ATTRIBUTENAME.nodeValue = newValue。
-
如果没有,请使用setAttribute()。