When to use setAttribute vs .attribute= in JavaScript?
Solution 1
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.
Solution 2
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
Solution 3
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.
Solution 4
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"
Solution 5
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 HTMLElement
s 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:
- Custom properties because they don't affect the DOM and are not attributes.
- Special mappings provided by the browser (
dir
,id
,className
). -
If attributes already exists,
element.attributes.ATTRIBUTENAME.nodeValue =
- setAttribute();
-
If attributes already exists,
element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
element.attributes.ATTRIBUTENAME = newNode
element.attributes.setNamedItem(ATTRIBUTENAME) = newNode
Conclusion (TL;DR)
-
Use the special property mappings from
HTMLElement
:element.dir
,element.id
,element.className
, orelement.lang
. -
If you are 100% sure the element is an extended
HTMLElement
with a special property, use that special mapping. (You can check withif (element instanceof HTMLAnchorElement)
). -
If you are 100% sure the attribute already exists, use
element.attributes.ATTRIBUTENAME.nodeValue = newValue
. -
If not, use
setAttribute()
.
Related videos on Youtube
Francisc
Updated on December 11, 2021Comments
-
Francisc over 2 years
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";
-
Andrew over 4 yearsWhen I switched from
.setAttribute()
to[key] = value
, everything started magically working.
-
-
olan over 10 years@Aerovistae - agree with you on this. Added a new answer which is hopefully clearer.
-
Michael over 10 yearsand furthermore, it appears after the last setAttribute in your example,
node.frameborder
is NOT defined, so you must getAttribute to get the value back. -
Michael over 10 yearsBut if you want to affect the innerHTML of the element, you have to use setAttribute...
-
olan over 10 years@Michael correct - if you use setAttribute to set a value, you must use getAttribute to retrieve it.
-
megawac about 10 yearsYou mean outterHTML* :)
-
aaaaaaaaaaaa almost 10 yearsThere 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-6423420 -
Plastic Rabbit over 9 yearsI found that a.href returns full url, but getAttribute('href') returns exactly what in that attribute (<a href="/help" ...).
-
Muhammad Umer about 9 yearswhat 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....
thenclearInterval(node.timerforclick)
-
Fareed Alnamrouti almost 9 yearsactually not really in Javascript you can do this x["aria-label"]
-
Antimony almost 9 years@fareednamrouti That doesn't work. I just tested it. The JS properties don't affect the html attributes. You really do need setAttribute here.
-
Fareed Alnamrouti almost 9 years@Antimony This is strange but Yes you are 100% right i will vote up
-
mseifert over 8 yearsThe
usemap
attribute can't be set using the dot notation when creating the map dynamically for an image. It requiresimg.setAttribute('usemap', "#MapName");
Does your answer imply thatusemap
is therefore "non-standard"? -
Mecki over 8 yearsThis should be the accepted answer. E.g. if you want to set
onchange
attribute of a DOM node, that only works withsetAttribute
. -
Ben about 8 yearsThis 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().
-
Ben about 8 yearsThis 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. -
jkdev about 8 yearsThanks for clearing this up. I'm curious, which versions of IE are considered 'modern' and follow the HTML spec?
-
Noitidart over 7 yearsThanks 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 thatsetAttribute
is preferred, avoiding the repaints? IsposElem.style.cssText = newStyle
more perfomrant thenposElem.style = newStyle
? -
Olgun Kaya almost 7 yearsI 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.
-
jgmjgm over 6 yearsAre you sure there's not ariaLabel?
-
Suraj Jain about 6 years@jkdev IE is never gets modern. What ever it gets to become old.
-
Suraj Jain about 6 yearsThanks 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.
-
Barzee about 5 yearsYou mentioned these four property mappings: dir, id, className, and lang. What about classList? Is classList a property mapping that is guaranteed to exist?
-
ShortFuse about 5 years
classList
is 100% guaranteed to exist, but it's not a string property, it's a liveDOMTokenList
object. Setting.className
directly is faster than manipulatingclassList
, but you'd overwriting the whole thing. -
Daniel Williams about 4 yearsHow about .value on <input> and <textarea> tags? What kind are they?
-
ShortFuse about 4 yearsThe ones mentioned in the answer are what W3C call "reflecting IDL attributes". When you change
.value
, you are changing the internal value of theHTMLInputElement
, which is then reflected on the attributes. They also don't have to bestring
..valueAsNumber
will changevalue
internally, and it'sstring
form will appear in thevalue
attribute. developer.mozilla.org/en-US/docs/Web/HTML/Attributes -
Heretic Monkey almost 4 yearsThis 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 setposElem.style = newStyle
, sinceposElem.style
is not a string-valued property. That why the author looks for thecssText
property onstyle
, which is a string-valued property ofstyle
. The attribute style is string-valued, but is parsed by the engine into thestyle
property. -
Heretic Monkey almost 4 yearsJSPerf is dead. Here's a MeasureThat.net test using the hidden Boolean attribute.
-
matty about 3 yearsThe question looks for insight into which of these methods are generally most appropriate to use. Does this answer that question?
-
Uyghur Lives Matter about 3 years@jgmjgm I just tested this on a
<select>
with anaria-label="..."
.x.ariaLabel
works on Chrome but isundefined
on Firefox. -
S.Serpooshan over 2 yearsThe question is if we shall use
element.setAttribute(x, value)
or directly as object propertieselement.x=value