【问题标题】:When to use setAttribute vs .attribute= in JavaScript?何时在 JavaScript 中使用 setAttribute 与 .attribute=?
【发布时间】:2011-04-24 13:46:32
【问题描述】:

是否已开发出使用 setAttribute 而非点 (.) 属性表示法的最佳实践?

例如:

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

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

【问题讨论】:

  • 当我从 .setAttribute() 切换到 [key] = value 时,一切都开始神奇地工作了。

标签: javascript attributes setattribute


【解决方案1】:

来自Javascript: The Definitive Guide,它澄清了一些事情。它指出 HTML 文档的 HTMLElement 对象定义了与所有标准 HTML 属性相对应的 JS 属性。

所以你只需要对非标准属性使用setAttribute

例子:

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

【讨论】:

  • 此外,它出现在您示例中的最后一个 setAttribute 之后,node.frameborder 未定义,因此您必须 getAttribute 才能取回该值。
  • @Michael 正确 - 如果您使用 setAttribute 设置值,则必须使用 getAttribute 来检索它。
  • 直接设置frameBorder没有错,但是注意大小写。有人认为将 HTML 属性的 JavaScript 等价物驼峰化是个好主意。我还没有找到任何规范,但网络似乎同意这是 12 个特定案例的问题(至少对于 HTML 4)。例如,请参阅以下帖子:drupal.org/node/1420706#comment-6423420
  • usemap 属性在为图像动态创建地图时无法使用点表示法设置。它需要img.setAttribute('usemap', "#MapName"); 你的回答是否暗示usemap 因此是“非标准的”?
  • 这大多是错误的。有些属性定义了属性,所以不要。这实际上只是关于他们如何编写规范。这与属性是否标准无关。但是,非标准属性确实只能通过 getAttribute() 来访问。
【解决方案2】:

之前的答案都不完整,而且大部分都包含错误信息。

在 JavaScript 中可以通过三种方式访问​​ DOM Element 的属性。只要您了解如何使用它们,这三种方法都可以在现代浏览器中可靠地工作。

1。 element.attributes

元素有一个属性attributes,它返回Attr 对象的实时NamedNodeMap。此集合的索引可能因浏览器而异。因此,订单无法保证。 NamedNodeMap 具有添加和删除属性的方法(分别为getNamedItemsetNamedItem)。

请注意,尽管 XML 明确区分大小写,但 DOM 规范要求 string names to be normalized,因此传递给 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>');
&lt;div class="class1" id="main" data-test="stuff" nonStandard="1234"&gt;&lt;/div&gt;

2。 element.getAttribute & element.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>');
&lt;div class="class1" id="main" data-test="stuff" nonStandard="1234"&gt;&lt;/div&gt;

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

可以使用 DOM 对象上的便捷属性来访问许多属性。给定对象上存在哪些属性取决于对象的 DOM 节点类型,而不管 HTML 中指定了哪些属性。可用属性在相关 DOM 对象的原型链中的某处定义。因此,定义的具体属性将取决于您访问的元素类型。

例如,classNameid 是在 Element 上定义的,并且存在于所有作为元素的 DOM 节点上,但不是文本或注释节点。 value 的定义更窄。它仅适用于 HTMLInputElement 及其后代。

请注意,JavaScript 属性区分大小写。虽然大多数属性将使用小写,但有些是驼峰式。因此,请务必检查规范以确保。

这个“图表”捕获了这些 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
&lt;div class="class1" id="main" data-test="stuff" nonStandard="1234"&gt;&lt;/div&gt;

警告:这是对 HTML 规范如何定义属性以及现代、常青浏览器如何处理它们的解释。肯定有一些旧的浏览器(IE、Netscape 等)不遵守甚至早于该规范。如果您需要支持旧的(损坏的)浏览器,您将需要比此处提供的更多信息。

【讨论】:

  • 感谢您解决这个问题。我很好奇,哪些版本的 IE 被认为是“现代”并遵循 HTML 规范?
  • @jkdev IE 永远不会变得现代。什么都会变老。
  • 感谢您提供如此详细的答案,我阅读了很多关于 DOM 和继承(如 HTMLElement 继承自 Element 等)的内容,您的回答非常有道理。
  • 该问题旨在深入了解这些方法中的哪些通常最适合使用。这能回答这个问题吗?
【解决方案3】:

如果您想在 JavaScript 中进行编程访问,您应该始终使用直接的 .attribute 表单(但请参阅下面的 quirksmode 链接)。它应该正确处理不同类型的属性(想想“onload”)。

当您希望按原样处理 DOM(例如,仅文字文本)时,请使用 getAttribute/setAttribute。不同的浏览器混淆了两者。见Quirks modes: attribute (in)compatibility

【讨论】:

  • @Aerovistae - 同意你的观点。添加了一个希望更清晰的新答案。
  • 但是如果要影响元素的innerHTML,就得使用setAttribute...
  • 你的意思是outterHTML* :)
  • 我发现 a.href 返回完整的 url,但 getAttribute('href') 返回的正是该属性中的内容(
  • 这个答案具有误导性。 getAttribute/setAttribute 不处理文字。它们只是访问相同信息的两种方式。在下面查看我的答案以获得完整的解释。
【解决方案4】:

我发现需要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"]
  • @fareednamrouti 那行不通。我刚刚测试了它。 JS 属性不影响 html 属性。这里确实需要 setAttribute。
  • @Antimony 这很奇怪,但是是的,你是 100% 正确的,我会投赞成票
  • 你确定没有 ariaLabel?
  • @jgmjgm 我刚刚在&lt;select&gt;aria-label="..." 上进行了测试。 x.ariaLabel 在 Chrome 上工作,但在 Firefox 上是 undefined
【解决方案5】:

这些答案并没有真正解决 propertiesattributes 之间的巨大混淆。此外,根据 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 更新 diridlangclassName

HTMLElement 的扩展

HTMLElement 具有这 4 个特殊属性。一些元素是HTMLElement 的扩展类,具有更多的映射属性。例如,HTMLAnchorElement 具有 HTMLAnchorElement.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)

  • 使用来自HTMLElement 的特殊属性映射:element.direlement.idelement.classNameelement.lang

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

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

  • 如果没有,请使用setAttribute()

【讨论】:

  • 您提到了这四个属性映射:dir、id、className 和 lang。类列表呢? classList 是保证存在的属性映射吗?
  • classList 100% 保证存在,但它不是字符串属性,它是一个活动的 DOMTokenList 对象。直接设置.className 比操作classList 更快,但你会覆盖整个事情。
  • 答案中提到的就是W3C所说的“反映IDL属性”。当您更改.value 时,您将更改HTMLInputElementinternal 值,该值随后会反映在属性上。他们也不必是string.valueAsNumber 将在 内部 更改 value,它的 string 形式将出现在 value 属性中。 developer.mozilla.org/en-US/docs/Web/HTML/Attributes
【解决方案6】:

“什么时候在 JavaScript 中使用 setAttribute 和 .attribute=?”

一般规则是使用.attribute 并检查它是否适用于浏览器。

..如果它可以在浏览器上运行,那么你很高兴。

..如果不是,请使用 .setAttribute(attribute, value) 而不是 .attribute 作为 that 属性。

对所有属性重复冲洗。

好吧,如果你很懒,你可以简单地使用.setAttribute。这在大多数浏览器上应该可以正常工作。 (虽然支持.attribute的浏览器可以比.setAttribute(attribute, value)优化得更好。)

【讨论】:

  • 问题是我们应该使用element.setAttribute(x, value)还是直接作为对象属性element.x=value
【解决方案7】:

这看起来像是使用 setAttribute 更好的一种情况:

Dev.Opera — Efficient 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);
}

【讨论】:

  • 感谢分享这个 tomo7,请您解释一下。 posElem.style = newStyle 是否不适用于所有浏览器(在 Firefox 中为我工作)?是否只是出于性能原因首选setAttribute,避免重绘? posElem.style.cssText = newStyleposElem.style = newStyle 性能更好吗?
  • 这与setAttribute vs .attribute无关; .style 是一个特殊属性,因为它是进入 CSSOM 的窗口。您只是不能设置posElem.style = newStyle,因为posElem.style 不是字符串值属性。这就是为什么作者在style 上寻找cssText 属性的原因,它是style 的字符串值属性。 attribute 样式是字符串值的,但由引擎解析为style 属性。
【解决方案8】:

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

我做了一个简单的基准测试 (here)

而且似乎 setAttributeNode 比使用 setAttribute 快 3 倍左右。

所以如果性能是一个问题 - 使用“setAttributeNode”

【讨论】:

  • 我认为您在 chrome 上运行测试。我使用 chrome、safari 和 firefox 在我的 mac 上进行了测试;预计其中 3 个显示 3 个不同的结果。
  • JSPerf 已死。 Here's a MeasureThat.net test 使用隐藏的布尔属性。
【解决方案9】:

来自Google API script 的关于此的有趣外卖:

他们这样做:

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" 研究项目的有用见解。如果我错了,请纠正我。

【讨论】:

    【解决方案10】:

    这是非常好的讨论。当我希望或可以说希望(我可能会成功添加)重新发明轮子时,我曾有过这样的时刻,无论是方形轮子。 上面的任何方法都是很好的讨论,所以任何人来这里寻找元素属性和属性之间的区别。这是我的一分钱,我确实必须努力找到它。我会保持简单,所以没有特别的技术术语。

    假设我们有一个名为“A”的变量。我们习惯的如下。

    下面会抛出一个错误,因为简单地说它是一种只能具有一个属性的对象,即奇异的左侧 = 奇异的右侧对象。其他所有东西都被忽略并扔进垃圾箱。

    let A = 'f';
    A.b =2;
    console.log(A.b);

    谁决定它必须是单数=单数。制定 JavaScript 和 html 标准以及引擎工作原理的人。

    让我们换个例子。

    let A = {};
    A.b =2;
    console.log(A.b);

    这一次它起作用了......因为我们已经明确告诉它,谁决定我们可以在这种情况下告诉它,但不能在以前的情况下。又是制定 JavaScript 和 html 标准的人。

    我希望我们能解决这个问题,让我们进一步复杂化

    let A = {};
    A.attribute ={};
    A.attribute.b=5;
    console.log(A.attribute.b); // will work
    
    console.log(A.b); // will not work

    我们所做的是第 1 层的树,然后是非奇异对象的子层。除非你知道什么在哪里并调用它,否则它不会起作用。

    这就是 HTMLDOM 在为每个 HTML 元素创建解析和绘制的 DOm 树时所发生的事情。每个人都有每个说的属性级别。有些是预定义的,有些不是。这就是 ID 和 VALUE 位出现的地方。 在幕后,它们在 1 级属性和太阳级属性(即属性)之间以 1:1 的比例映射。因此,改变一个会改变另一个。这是对象 getter 和 setter 方案所起的作用。

    let A = {
    attribute :{
    id:'',
    value:''
    },
    getAttributes: function (n) {
        return this.attribute[n];
      },
    setAttributes: function (n,nn){
    this.attribute[n] = nn;
    if(this[n]) this[n] = nn;
    },
    id:'',
    value:''
    };
    
    
    
    A.id = 5;
    console.log(A.id);
    console.log(A.getAttributes('id'));
    
    A.setAttributes('id',7)
    console.log(A.id);
    console.log(A.getAttributes('id'));
    
    A.setAttributes('ids',7)
    console.log(A.ids);
    console.log(A.getAttributes('ids'));
    
    A.idsss=7;
    console.log(A.idsss);
    console.log(A.getAttributes('idsss'));

    这就是上面显示的点,ELEMENTS 有另一组所谓的属性列表属性,它有自己的主要属性。两者之间有一些预定义的属性,并被映射为 1:1,例如ID 对每个人都是通用的,但 value 不是,src 也不是。当解析器到达那个点时,它会简单地拉出字典,了解什么时候存在这样的东西。

    所有元素都有属性和属性,并且它们之间的一些项目是通用的。一个人的共同点在另一个人身上不常见。

    在 HTML3 的旧时代,我们先使用 html,然后再使用 JS。现在,它反过来了,因此使用内联 onlclick 变得如此可恶。事情在 HTML5 中取得了进展,其中有许多可以作为集合访问的属性列表,例如类,风格。在过去,颜色是一个属性,现在移到 css 进行处理不再是有效的属性。

    Element.attributes 是 Element 属性中的子属性列表。

    除非您可以更改 Element 属性的 getter 和 setter,这几乎是不可能的,因为它会破坏所有功能通常不能立即写入,因为我们将某些东西定义为 A.item 并不一定意味着 Js 引擎会还运行另一行函数将其添加到 Element.attributes.item 中。

    我希望这能在澄清什么是什么方面取得一些进展。 为此,我尝试了 Element.prototype.setAttribute 和自定义函数,它只是打破了整个事情,因为它覆盖了设置属性函数在幕后播放的原生函数。

    【讨论】:

      【解决方案11】:

      再增加2个与.textContent.innerHTML相关的点

      <div id="mydiv"></div>
      
      
      var elem = document.getElementById("mydiv");
      
      elem.textContent = "hello"; // OK - Content has been updated
      elem.setAttribute("textContent", "hello"); // NOT OK - You are trying to set
                                                 // the attribute than it's content
      
      elem.innerHTML = "world";   // OK - Content has been updated
      elem.setAttribute("innerHTML", "world"); // NOT OK - You are trying to set
                                               // the attribute than it's content
      

      【讨论】:

        【解决方案12】:

        两者之间的一个区别是 setAttribute,当用于设置 &lt;input/&gt;value 时,当您在其所属的表单上调用 .reset() 时,将使其成为默认值,但 .value = 不会这样做。

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

        请注意,如果调用 setAttribute() 来设置特定属性的值,则后续调用 reset() 不会将该属性重置为其默认值,而是将属性保持在 setAttribute( ) 调用将其设置为。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-04-04
          • 1970-01-01
          • 2016-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多