什么时候在JavaScript中使用setAttribute vs .attribute =?


234

是否已开发出围绕使用setAttribute而不是点(.)属性符号的最佳实践?

例如:

myObj.setAttribute("className", "nameOfClass");
myObj.setAttribute("id", "someID");

要么

myObj.className = "nameOfClass";
myObj.id = "someID";

1
当我从切换.setAttribute()到时[key] = value,一切开始神奇地工作。
安德鲁(Andrew)

Answers:


73

如果要使用.attributeJavaScript进行编程访问,则应始终使用直接形式(但请参见下面的quirksmode链接)。它应该正确处理不同类型的属性(请考虑“ onload”)。

当您希望按原样处理DOM时,请使用getAttribute/ setAttribute(例如,仅文字文本)。不同的浏览器将两者混淆。请参阅怪癖模式:属性(兼容性)


127
这个答案还不够清楚...我还真的不明白这一点。
临时用户名

1
@Aerovistae-在这一点上同意你的观点。添加了一个新的答案,希望它更加清晰。
olan 2014年

1
但是,如果要影响元素的innerHTML,则必须使用setAttribute ...
迈克尔

3
您的意思是outterHTML * :)
megawac 2014年

4
我发现a.href返回完整的url,但是getAttribute('href')恰好返回该属性中的内容(<a href =“ / help” ...)。
塑胶兔子

144

Javascript:权威指南中,它可以使事情变得清晰。它注意到HTML文档的HTMLElement对象定义了与所有标准HTML属性相对应的JS属性。

因此,您只需要setAttribute用于非标准属性。

例:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works

2
而且,它没有出现在示例中的最后一个setAttribute之后node.frameborder,因此您必须使用getAttribute才能取回值。
2014年

5
@Michael正确-如果使用setAttribute设置值,则必须使用getAttribute检索它。
olan 2014年

3
frameBorder直接设置没有任何问题,但请注意大写。有人认为驼峰写出HTML属性的JavaScript等效项是个好主意。我没有为此找到任何规范,但是网络似乎同意这是12种特定情况的问题(至少对于HTML 4)。例如,请参见以下帖子:drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaaaa

1
usemap属性不能用于图像动态创建地图时使用点符号来设置。它要求img.setAttribute('usemap', "#MapName");您的答案是否暗示usemap因此是“非标准”?
mseifert '16

1
这主要是错误的。有些属性已定义属性,所以没有。实际上,这只是他们编写规范的方式。它与属性是否为标准无关。但是,确实不能使用getAttribute()访问非标准属性。

79

先前的答案均不完整,且大多数包含错误信息。

有三种访问JavaScript中DOM 元素的属性的方法。只要您了解如何使用它们,这三者就可以在现代浏览器中可靠地工作。

1。 element.attributes

元素有一个属性的属性,它返回一个活的NamedNodeMap的Attr对象。在浏览器之间,此集合的索引可能有所不同。因此,不能保证顺序。NamedNodeMap具有添加和删除属性的方法(分别为getNamedItemsetNamedItem)。

请注意,尽管XML明确区分大小写,但DOM规范要求将字符串名称标准化,因此传递给getNamedItem其的名称实际上不区分大小写。

用法示例:

var div = document.getElementsByTagName('div')[0];

//you can look up specific attributes
var classAttr = div.attributes.getNamedItem('CLASS');
document.write('attributes.getNamedItem() Name: ' + classAttr.name + ' Value: ' + classAttr.value + '<br>');

//you can enumerate all defined attributes
for(var i = 0; i < div.attributes.length; i++) {
  var attr = div.attributes[i];
  document.write('attributes[] Name: ' + attr.name + ' Value: ' + attr.value + '<br>');
}

//create custom attribute
var customAttr = document.createAttribute('customTest');
customAttr.value = '567';
div.attributes.setNamedItem(customAttr);

//retreive custom attribute
customAttr = div.attributes.getNamedItem('customTest');
document.write('attributes.getNamedItem() Name: ' + customAttr.name + ' Value: ' + customAttr.value + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

2. element.getAttributeelement.setAttribute

这些方法直接存在于上,Element无需访问attributes及其方法,但执行相同的功能。

同样,请注意字符串名称不区分大小写。

用法示例:

var div = document.getElementsByTagName('div')[0];

//get specific attributes
document.write('Name: class Value: ' + div.getAttribute('class') + '<br>');
document.write('Name: ID Value: ' + div.getAttribute('ID') + '<br>');
document.write('Name: DATA-TEST Value: ' + div.getAttribute('DATA-TEST') + '<br>');
document.write('Name: nonStandard Value: ' + div.getAttribute('nonStandard') + '<br>');


//create custom attribute
div.setAttribute('customTest', '567');

//retreive custom attribute
document.write('Name: customTest Value: ' + div.getAttribute('customTest') + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

3. DOM对象的属性,例如 element.id

可以使用DOM对象上的便捷属性来访问许多属性。存在哪些属性取决于DOM节点的类型,而不取决于HTML中定义的属性。这些属性是在所讨论的DOM对象原型链中的某个位置定义的。定义的特定属性将取决于您正在访问的Element的类型。例如,className和在所有作为元素的DOM节点(即不是文本或注释节点)上id定义Element并存在。但是value更狭窄。它HTMLInputElement在其他元素上定义且可能不存在。

请注意,JavaScript属性区分大小写。尽管大多数属性将使用小写字母,但有些是camelCase。因此,请务必检查规格以确保。

此“图表”捕获了这些DOM对象的一部分原型链。它甚至还没有完成,但是它捕获了整个结构。

                      ____________Node___________
                      |               |         |
                   Element           Text   Comment
                   |     |
           HTMLElement   SVGElement
           |         |
HTMLInputElement   HTMLSpanElement

用法示例:

var div = document.getElementsByTagName('div')[0];

//get specific attributes
document.write('Name: class Value: ' + div.className + '<br>');
document.write('Name: id Value: ' + div.id + '<br>');
document.write('Name: ID Value: ' + div.ID + '<br>'); //undefined
document.write('Name: data-test Value: ' + div.dataset.test + '<br>'); //.dataset is a special case
document.write('Name: nonStandard Value: ' + div.nonStandard + '<br>'); //undefined
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

警告:这是对HTML规范定义和现代浏览器如何处理属性的解释。我没有尝试解决过时的浏览器的局限性。如果您需要支持旧版浏览器,则除了这些信息外,您还需要了解这些浏览器中的损坏之处。


感谢您清理此问题。我很好奇,哪个版本的IE被视为“现代”版本并遵循HTML规范?
jkdev

3
@jkdev IE永远不会变得现代。它变老了。
Suraj Jain

感谢您提供如此详细的答案,我阅读了很多有关DOM和继承的信息,例如HTMLElement从Element继承等等,您的答案很有意义。
Suraj Jain

16

我发现有一种情况setAttribute是更改ARIA属性时有必要,因为没有相应的属性。例如

x.setAttribute('aria-label', 'Test');
x.getAttribute('aria-label');

没有x.arialabel或没有类似的东西,因此您必须使用setAttribute。

编辑:x [“ aria-label”]不起作用。您确实确实需要setAttribute。

x.getAttribute('aria-label')
null
x["aria-label"] = "Test"
"Test"
x.getAttribute('aria-label')
null
x.setAttribute('aria-label', 'Test2')
undefined
x["aria-label"]
"Test"
x.getAttribute('aria-label')
"Test2"

实际上不是真的可以使用Javascript来实现x [“ aria-label”]
Fareed Alnamrouti 2015年

@fareednamrouti不起作用。我刚刚测试过。JS属性不会影响html属性。您确实确实需要setAttribute在这里。

@锑这很奇怪,但是是的,你是100%正确,我会投票
Fareed Alnamrouti 2015年

2
您确定没有ariaLabel吗?
jgmjgm

8

这些答案并没有真正解决属性属性之间的巨大混淆。另外,根据Java语言原型,有时您可以使用元素的属性来访问属性,有时则不能。

首先,您必须记住an HTMLElement是一个Javascript对象。像所有对象一样,它们具有属性。当然,您可以创建一个几乎称为您想要的任何内部属性HTMLElement,但它不必对DOM做任何事情(页面上的内容)。点符号(.)用于属性。现在,有一些特殊的属性映射到属性,并在当时或写,只有4所保证的(稍后更多)。

所有都HTMLElement包含一个名为的属性attributesHTMLElement.attributes是与DOM中的元素相关的活动 NamedNodeMap对象。“实时”表示当节点在DOM中更改时,它们在JavaScript端更改,反之亦然。在这种情况下,DOM属性就是所讨论的节点。A 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 || ''

}

因此,您可以通过dir6种方式更改属性:

  // 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的所有属性,但只diridlang,并className与方法#6。

HTMLElement的扩展

HTMLElement具有这4个特殊属性。一些元素是HTMLElement具有更多映射属性的扩展类。例如,HTMLAnchorElementHTMLAnchorElement.hrefHTMLAnchorElement.relHTMLAnchorElement.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 。基本上,为了:

  1. 自定义属性,因为它们不影响DOM且不是属性
  2. 浏览器提供特殊映射(diridclassName)。
  3. 如果属性已经存在element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute();
  5. 如果属性已经存在element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

结论(TL; DR)

  • 从使用特殊的属性映射HTMLElementelement.direlement.idelement.className,或element.lang

  • 如果您100%确定元素是HTMLElement具有特殊属性的扩展,请使用该特殊映射。(您可以通过检查if (element instanceof HTMLAnchorElement))。

  • 如果您100%确定该属性已经存在,请使用element.attributes.ATTRIBUTENAME.nodeValue = newValue

  • 如果不是,请使用setAttribute()


您提到了这四个属性映射:dir,id,className和lang。那classList呢?classList是否可以保证存在属性映射?
巴泽

classList100%保证存在,但这不是字符串属性,而是活动DOMTokenList对象。.className直接设置比操作classList要快,但是您会覆盖整个内容。
ShortFuse

<input>和<textarea>标记上的.value怎么样?他们是什么样的人?
Daniel Williams

答案中提到的是W3C所谓的“反射IDL属性”。更改时.value,您正在更改的内部HTMLInputElement,该值随后会反映在属性上。他们也不必如此string.valueAsNumber将在value 内部进行更改,其string形式将出现在value属性中。 developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse

3

“何时在JavaScript中使用setAttribute vs .attribute =?

一般规则是使用.attribute并检查它是否在浏览器上有效。

..如果它可以在浏览器上运行,那就很好了。

..如果.setAttribute(attribute, value)不是,.attribute对该属性使用代替。

冲洗重复所有属性。

好吧,如果您很懒,可以直接使用.setAttribute。在大多数浏览器上应该可以正常工作。(尽管支持.attribute该功能的浏览器可以比进行优化.setAttribute(attribute, value)。)


0

在一种情况下,最好使用setAttribute:

Dev.Opera —高效的JavaScript

var posElem = document.getElementById('animation');
var newStyle = 'background: ' + newBack + ';' +
'color: ' + newColor + ';' +
    'border: ' + newBorder + ';';
if(typeof(posElem.style.cssText) != 'undefined') {
    posElem.style.cssText = newStyle;
} else {
    posElem.setAttribute('style', newStyle);
}

2
感谢您分享此tomo7,请您多解释一下。是否posElem.style = newStyle在所有浏览器(Firefox中为我工作)不工作?是否仅出于性能原因而setAttribute避免重涂?是posElem.style.cssText = newStyle更perfomrant然后posElem.style = newStyle
Noitidart '16

0

在元素上设置属性(例如类)的方法:1. el.className =字符串2. el.setAttribute('class',string)3. el.attributes.setNamedItem(object)4. el.setAttributeNode(node)

我做了一个简单的基准测试(在这里

似乎setAttributeNode比使用setAttribute快约3倍。

因此,如果性能存在问题,请使用“ setAttributeNode”


我认为您在chrome上进行了测试。我在Mac上使用Chrome,Safari和Firefox浏览器;预期其中3个会显示3个不同的结果。
奥尔贡·卡亚

0

Google API脚本中获得的有趣的信息:

他们这样做:

var scriptElement = document.createElement("script");
scriptElement = setAttribute("src", "https://some.com");
scriptElement = setAttribute("nonce", "https://some.com");
scriptElement.async = "true";

注意,它们如何setAttribute用于“ src”和“ nonce”,然后.async = ...用于“ async”属性。

我不确定100%,但这可能是因为“异步”仅在支持直接.attr =分配的浏览器中受支持。因此,没有任何尝试的理由,sestAttribute("async")因为如果浏览器不理解.async=...,它将无法理解“异步”属性。

希望这对我正在进行的“ Un-minify GAPI”研究项目有帮助。如我错了请纠正我。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.