When to use setAttribute vs .attribute= in JavaScript?
Asked Answered
D

14

315

Has a best-practice around using setAttribute instead of the dot (.) attribute notation been developed?

E.g.:

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

or

myObj.className = "nameOfClass";
myObj.id = "someID";
Displode answered 12/10, 2010 at 21:45 Comment(1)
When I switched from .setAttribute() to [key] = value, everything started magically working.Toomay
A
78

You should always use the direct .attribute form (but see the quirksmode link below) if you want programmatic access in JavaScript. It should handle the different types of attributes (think "onload") correctly.

Use getAttribute/setAttribute when you wish to deal with the DOM as it is (e.g. literal text only). Different browsers confuse the two. See Quirks modes: attribute (in)compatibility.

Arenaceous answered 12/10, 2010 at 21:49 Comment(5)
@Aerovistae - agree with you on this. Added a new answer which is hopefully clearer.Grubbs
But if you want to affect the innerHTML of the element, you have to use setAttribute...Psychedelic
You mean outterHTML* :)Adeline
I found that a.href returns full url, but getAttribute('href') returns exactly what in that attribute (<a href="/help" ...).Hollowell
This answer is misleading. getAttribute/setAttribute do not deal in literal text. They are just two ways of accessing the same information. Check out my answer below for a complete explanation.Smokedry
G
199

From Javascript: The Definitive Guide, it clarifies things. It notes that HTMLElement objects of a HTML doc define JS properties that correspond to all standard HTML attributes.

So you only need to use setAttribute for non-standard attributes.

Example:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
Grubbs answered 10/1, 2014 at 12:47 Comment(7)
and furthermore, it appears after the last setAttribute in your example, node.frameborder is NOT defined, so you must getAttribute to get the value back.Psychedelic
@Psychedelic correct - if you use setAttribute to set a value, you must use getAttribute to retrieve it.Grubbs
There is nothing wrong with setting frameBorder directly, but note the capitalization. Someone thought it was a jolly good idea to camelCase the JavaScript equivalents of HTML attributes. I haven't managed to find any specification for this, but the net seems to agree that it is a matter of 12 specific cases (for HTML 4 at least). See for instance the following post: drupal.org/node/1420706#comment-6423420Nocturne
what do you mean by doesn't work. Like it doesn't show up in dom...what if that's fine with you and you are justing using it to store say timers in it. node.timerforclick=fun.... then clearInterval(node.timerforclick)Decorate
The usemap attribute can't be set using the dot notation when creating the map dynamically for an image. It requires img.setAttribute('usemap', "#MapName"); Does your answer imply that usemap is therefore "non-standard"?Chaste
This should be the accepted answer. E.g. if you want to set onchange attribute of a DOM node, that only works with setAttribute.Speechmaker
This mostly wrong. Some attributes has properties defined, so do not. It's really just about how they wrote the spec. It's has nothing to to due with the attributes being standard or not. However, it is true that none-standard properties can only be accessed with getAttribute().Smokedry
S
120

None of the previous answers are complete and most contain misinformation.

There are three ways of accessing the attributes of a DOM Element in JavaScript. All three work reliably in modern browsers as long as you understand how to utilize them.

1. element.attributes

Elements have a property attributes that returns a live NamedNodeMap of Attr objects. The indexes of this collection may be different among browsers. So, the order is not guaranteed. NamedNodeMap has methods for adding and removing attributes (getNamedItem and setNamedItem, respectively).

Notice that though XML is explicitly case sensitive, the DOM spec calls for string names to be normalized, so names passed to getNamedItem are effectively case insensitive.

Example Usage:

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.getAttribute & element.setAttribute

These methods exist directly on the Element without needing to access attributes and its methods but perform the same functions.

Again, notice that string name are case insensitive.

Example Usage:

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. Properties on the DOM object, such as element.id

Many attributes can be accessed using convenient properties on the DOM object. Which properties exist on a given object depends on the object's DOM node type, regardless of which attributes are specified in the HTML. The available properties are defined somewhere in the prototype chain of DOM object in question. So, the specific properties that are defined will depend on the type of Element you are accessing.

For example, className and id are defined on Element and exist on all DOM nodes that are elements, but not text or comment nodes. value is more narrowly defined. It only available on HTMLInputElement and it's descendants.

Notice that JavaScript properties are case sensitive. Although most properties will use lowercase, some are camelCase. So always check the spec to be sure.

This "chart" captures a portion of the prototype chain for these DOM objects. It's not even close to complete, but it demonstrates the overall structure.

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

Example Usage:

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>

Caveat: This is an explanation of how the HTML spec define attributes and how modern, evergreen browsers handle them. There certainly are old browsers (IE, Netscape, etc.) that didn't adhere to or even predated the spec. If you need to support old ancient (broken) browsers, you'll need more information than provided here.

Smokedry answered 12/4, 2016 at 18:53 Comment(4)
Thanks for clearing this up. I'm curious, which versions of IE are considered 'modern' and follow the HTML spec?Ludhiana
@Ludhiana IE is never gets modern. What ever it gets to become old.Paez
Thanks for such an detailed answer, I read a lot about DOM and inheritance like HTMLElement inherit from Element and so on, your answer makes perfect sense.Paez
The question looks for insight into which of these methods are generally most appropriate to use. Does this answer that question?Cholula
A
78

You should always use the direct .attribute form (but see the quirksmode link below) if you want programmatic access in JavaScript. It should handle the different types of attributes (think "onload") correctly.

Use getAttribute/setAttribute when you wish to deal with the DOM as it is (e.g. literal text only). Different browsers confuse the two. See Quirks modes: attribute (in)compatibility.

Arenaceous answered 12/10, 2010 at 21:49 Comment(5)
@Aerovistae - agree with you on this. Added a new answer which is hopefully clearer.Grubbs
But if you want to affect the innerHTML of the element, you have to use setAttribute...Psychedelic
You mean outterHTML* :)Adeline
I found that a.href returns full url, but getAttribute('href') returns exactly what in that attribute (<a href="/help" ...).Hollowell
This answer is misleading. getAttribute/setAttribute do not deal in literal text. They are just two ways of accessing the same information. Check out my answer below for a complete explanation.Smokedry
A
25

One case I found where setAttribute is necessary is when changing ARIA attributes, since there are no corresponding properties. For example

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

There's no x.arialabel or anything like that, so you have to use setAttribute.

Edit: x["aria-label"] does not work. You really do need 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"
Aubree answered 5/1, 2015 at 0:54 Comment(5)
actually not really in Javascript you can do this x["aria-label"]Organ
@fareednamrouti That doesn't work. I just tested it. The JS properties don't affect the html attributes. You really do need setAttribute here.Aubree
@Aubree This is strange but Yes you are 100% right i will vote upOrgan
Are you sure there's not ariaLabel?Emotionality
@Emotionality I just tested this on a <select> with an aria-label="...". x.ariaLabel works on Chrome but is undefined on Firefox.Isaisaac
H
23

These answers aren't really addressing the large confusion with between properties and attributes. Also, depending on the Javascript prototype, sometimes you can use a an element's property to access an attributes and sometimes you can't.

First, you have to remember that an HTMLElement is a Javascript object. Like all objects, they have properties. Sure, you can create a property called nearly anything you want inside HTMLElement, but it doesn't have to do anything with the DOM (what's on the page). The dot notation (.) is for properties. Now, there some special properties that are mapped to attributes, and at the time or writing there are only 4 that are guaranteed (more on that later).

All HTMLElements include a property called attributes. HTMLElement.attributes is a live NamedNodeMap Object that relates to the elements in the DOM. "Live" means that when the node changes in the DOM, they change on the JavaScript side, and vice versa. DOM attributes, in this case, are the nodes in question. A Node has a .nodeValue property that you can change. NamedNodeMap objects have a function called setNamedItem where you can change the entire node. You can also directly access the node by the key. For example, you can say .attributes["dir"] which is the same as .attributes.getNamedItem('dir'); (Side note, NamedNodeMap is case-insensitive, so you can also pass 'DIR');

There's a similar function directly in HTMLElement where you can just call setAttribute which will automatically create a node if it doesn't exist and set the nodeValue. There are also some attributes you can access directly as properties in HTMLElement via special properties, such as dir. Here's a rough mapping of what it looks like:

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 || ''

}

So you can change the dir attributes 6 ways:

  // 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';

You can update all properties with methods #1-5, but only dir, id, lang, and className with method #6.

Extensions of HTMLElement

HTMLElement has those 4 special properties. Some elements are extended classes of HTMLElement have even more mapped properties. For example, HTMLAnchorElement has HTMLAnchorElement.href, HTMLAnchorElement.rel, and HTMLAnchorElement.target. But, beware, if you set those properties on elements that do not have those special properties (like on a HTMLTableElement) then the attributes aren't changed and they are just, normal custom properties. To better understand, here's an example of its inheritance:

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 || '' 
}

Custom Properties

Now the big warning: Like all Javascript objects, you can add custom properties. But, those won't change anything on the DOM. You can do:

  const newElement = document.createElement('div');
  // THIS WILL NOT CHANGE THE ATTRIBUTE
  newElement.display = 'block';

But that's the same as

  newElement.myCustomDisplayAttribute = 'block';

This means that adding a custom property will not be linked to .attributes[attr].nodeValue.

Performance

I've built a jsperf test case to show the difference: https://jsperf.com/set-attribute-comparison. Basically, In order:

  1. Custom properties because they don't affect the DOM and are not attributes.
  2. Special mappings provided by the browser (dir, id, className).
  3. If attributes already exists, element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute();
  5. If attributes already exists, element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Conclusion (TL;DR)

  • Use the special property mappings from HTMLElement: element.dir, element.id, element.className, or element.lang.

  • If you are 100% sure the element is an extended HTMLElement with a special property, use that special mapping. (You can check with if (element instanceof HTMLAnchorElement)).

  • If you are 100% sure the attribute already exists, use element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • If not, use setAttribute().

Haematothermal answered 14/4, 2019 at 20:1 Comment(4)
You mentioned these four property mappings: dir, id, className, and lang. What about classList? Is classList a property mapping that is guaranteed to exist?Totemism
classList is 100% guaranteed to exist, but it's not a string property, it's a live DOMTokenList object. Setting .className directly is faster than manipulating classList, but you'd overwriting the whole thing.Haematothermal
How about .value on <input> and <textarea> tags? What kind are they?Walhalla
The ones mentioned in the answer are what W3C call "reflecting IDL attributes". When you change .value, you are changing the internal value of the HTMLInputElement, which is then reflected on the attributes. They also don't have to be string. .valueAsNumber will change value internally, and it's string form will appear in the value attribute. developer.mozilla.org/en-US/docs/Web/HTML/AttributesHaematothermal
T
2

"When to use setAttribute vs .attribute= in JavaScript?"

A general rule is to use .attribute and check if it works on the browser.

..If it works on the browser, you're good to go.

..If it doesn't, use .setAttribute(attribute, value) instead of .attribute for that attribute.

Rinse-repeat for all attributes.

Well, if you're lazy you can simply use .setAttribute. That should work fine on most browsers. (Though browsers that support .attribute can optimize it better than .setAttribute(attribute, value).)

Tjaden answered 13/4, 2017 at 23:57 Comment(1)
The question is if we shall use element.setAttribute(x, value) or directly as object properties element.x=valueVernal
C
2

Interesting observation from Google API script regarding this:

They do it like this:

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

Notice, how they use setAttribute for "src" and "nonce", but then .async = ... for "async" attribute.

I'm not 100% sure, but probably that's because "async" is only supported on browsers that support direct .attr = assignment. So, there's no sense trying to setAttribute("async") because if browser doesn't understand .async=... - it will not understand "async" attribute.

Hopefully, that's a helpful insight from my ongoing "Un-minify GAPI" research project. Correct me if I'm wrong.

Capers answered 6/5, 2020 at 10:20 Comment(0)
W
0

This looks like one case where it is better to use 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);
}
Wombat answered 11/12, 2014 at 7:55 Comment(2)
Thanks for sharing this tomo7, may you please explain a bit more. Does posElem.style = newStyle not work in all browsers (worked for me in Firefox)? Is it just for performance reasons that setAttribute is preferred, avoiding the repaints? Is posElem.style.cssText = newStyle more perfomrant then posElem.style = newStyle?Exhibitionist
This has nothing to do with setAttribute vs .attribute; .style is a special property because it's a window into the CSSOM. You simply can't set posElem.style = newStyle, since posElem.style is not a string-valued property. That why the author looks for the cssText property on style, which is a string-valued property of style. The attribute style is string-valued, but is parsed by the engine into the style property.Drogue
P
0

methods for setting attributes(for example class) on an element: 1. el.className = string 2. el.setAttribute('class',string) 3. el.attributes.setNamedItem(object) 4. el.setAttributeNode(node)

I have made a simple benchmark test (here)

and it seems that setAttributeNode is about 3 times faster then using setAttribute.

so if performance is an issue - use "setAttributeNode"

Psychopathist answered 9/5, 2017 at 22:24 Comment(2)
I think you run the test on chrome. I teted on my mac using chrome, safari and firefox; expectedly 3 of them showed 3 different results.Arvad
JSPerf is dead. Here's a MeasureThat.net test using the hidden Boolean attribute.Drogue
S
0

This is very good discussion. I had one of those moments when I wished or lets say hoped (successfully that I might add) to reinvent the wheel be it a square one. Any ways above is good discussion, so any one coming here looking for what is the difference between Element property and attribute. here is my penny worth and I did have to find it out hard way. I would keep it simple so no extraordinary tech jargon.

suppose we have a variable calls 'A'. what we are used to is as following.

Below will throw an error because simply it put its is kind of object that can only have one property and that is singular left hand side = singular right hand side object. Every thing else is ignored and tossed out in bin.

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

who has decided that it has to be singular = singular. People who make JavaScript and html standards and thats how engines work.

Lets change the example.

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

This time it works ..... because we have explicitly told it so and who decided we can tell it in this case but not in previous case. Again people who make JavaScript and html standards.

I hope we are on this lets complicate it further

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

console.log(A.b); // will not work

What we have done is tree of sorts level 1 then sub levels of non-singular object. Unless you know what is where and and call it so it will work else no.

This is what goes on with HTMLDOM when its parsed and painted a DOm tree is created for each and every HTML ELEMENT. Each has level of properties per say. Some are predefined and some are not. This is where ID and VALUE bits come on. Behind the scene they are mapped on 1:1 between level 1 property and sun level property aka attributes. Thus changing one changes the other. This is were object getter ans setter scheme of things plays role.

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'));

This is the point as shown above ELEMENTS has another set of so called property list attributes and it has its own main properties. there some predefined properties between the two and are mapped as 1:1 e.g. ID is common to every one but value is not nor is src. when the parser reaches that point it simply pulls up dictionary as to what to when such and such are present.

All elements have properties and attributes and some of the items between them are common. What is common in one is not common in another.

In old days of HTML3 and what not we worked with html first then on to JS. Now days its other way around and so has using inline onlclick become such an abomination. Things have moved forward in HTML5 where there are many property lists accessible as collection e.g. class, style. In old days color was a property now that is moved to css for handling is no longer valid attribute.

Element.attributes is sub property list with in Element property.

Unless you could change the getter and setter of Element property which is almost high unlikely as it would break hell on all functionality is usually not writable off the bat just because we defined something as A.item does not necessarily mean Js engine will also run another line of function to add it into Element.attributes.item.

I hope this gives some headway clarification as to what is what. Just for the sake of this I tried Element.prototype.setAttribute with custom function it just broke loose whole thing all together, as it overrode native bunch of functions that set attribute function was playing behind the scene.

Shuster answered 14/1, 2021 at 12:59 Comment(0)
H
0

Adding 2 more points related to .textContent and .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
Herwick answered 29/11, 2021 at 18:42 Comment(0)
L
0

One difference between the two is that setAttribute, when used to set the value of an <input/> will make that the default value when you call .reset() on the form it's part of, but .value = will not do this.

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

Note that if setAttribute() is called to set the value of a particular attribute, a subsequent call to reset() won’t reset the attribute to its default value, but instead will keep the attribute at whatever value the setAttribute() call set it to.

Loco answered 10/12, 2021 at 19:42 Comment(0)
M
0

If the element you are referring to does not already include a Javascript object property for a given attribute (as others have described), then setting that property will not propagate the change back to the DOM, it just adds the named property to the Javascript object, and the DOM ignores it. For example, getting the element mySpan by id and then doing mySpan.class = 'warning' will do nothing, whether or not the span element in question already has a class attribute defined, because mySpan.class is not defined in the Javascript object for a span element. You have to use mySpan.setAttribute('class', 'warning').

However a second nuance is that setting the Javascript object's innerHTML property using mySpan.setAttribute("innerHTML", someHTML) does not update the content of the element. I don't know how Javascript even catches mySpan.innerHTML = something, and calls the HTML parser, but there's some magic going on under the hood.

Maplemaples answered 27/7, 2022 at 2:45 Comment(0)
B
0

For an <img /> element, the results differ:

let img = document.getElementById('img');
console.log(img.src);
console.log(img.getAttribute('src'));
<img id="img" src="/favicon.ico" />
Bleachers answered 11/4 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.