The DOM is just a little weird about some things, and the way you deal with attributes is no exception. There are a number of ways to deal with the attributes on elements. By attributes, I mean things like the id
in <div id="cool"></div>
. Sometimes you need to set them. Sometimes you need to get them. Sometimes there are fancy helper APIs. Sometimes there isn’t.
For this article, I’ll assume el
is a DOM element in your JavaScript. Let’s say you’ve done something like const el = document.querySelector("#cool");
and matched <div id="cool">
or whatever.
Some attributes are also attributes of the DOM object itself, so iff you need to set an id
or title
, you can do:
el.id; // "cool"
el.title = "my title";
el.title; // "my title";
Others that work like that are lang
, align
, and all the big events, like onclick
.
Then there are attributes that work similarly to that but are nested deeper. The style
attribute is like that. If you log el.style
you’ll see a ton of CSS style declarations. You can get and set them easily:
el.style.color = "red";
module.style.backgroundColor = "black";
You can get computed colors this way too. If you do module.style.color
hoping to get the color of an element out of the gate, you probably won’t get it. For that, you’d have to do:
let style = window.getComputedStyle(el);
style.color; // whatever in CSS won out
But not all attributes are like first-class attributes like that.
el['aria-hidden'] = true; // nope
That “works” in that it sets that as a property, but it doesn’t set it in the DOM the proper way. Instead, you’ll have to use the generic setter and getter functions that work for all attributes, like:
el.setAttribute("aria-hidden", true);
el.getAttribute("aria-hidden");
Some attributes have fancy helpers. The most fancy is classList
for class attributes. On an element like:
<div class="module big"></div>
You’d have:
el.classList.value; // "module big"
el.classList.length; // 2
el.classList.add("cool"); // adds the class "cool", so "module big cool"
el.classList.remove("big"); // removes "big", so "module cool"
el.classList.toggle("big"); // adds "big" back, because it was missing (goes back and forth)
el.classList.contains("module"); // true
There’s even more, and classList
itself behaves like an array so you can forEach
it and such. That’s a pretty strong reason to use classes, as the DOM API around them is so handy.
Another attribute type that has a somewhat fancy help is data-*
. Say you’ve got:
<div data-active="true" data-placement="top right" data-extra-words="hi">test</div>
You’ve got dataset
:
el.dataset;
/*
{
active: "true",
"placement", "top right"
*/
el.dataset.active; // "true"
el.dataset.extraWords; // "hi", note the conversion to camelCase
el.dataset.active = "false"; // setters work like this
I think you need to add the example:
el.ariaHidden = true;
“…classList itself behaves like an array so you can forEach it and such”
I think this sentence is a bit misleading as
classList
is a live DOMTokenList collection not a real array, so, for example, you can use aforEach
method on it but you can’t use other array related methods such asmap
,filter
,reduce
etc…, without converting it into the real array first.And then we have .setAttributeNS and .getAttributeNS if we have to deal with SVG…
Is there any performance difference between
el.setAttribute("aria-hidden", true);
andel.ariaHidden = true;
?Others to be considered:
htmlFor
for<label for="asdf">
backgroundColor
forbackground-color
butreadOnly
not forread-only
andcontentEditable
not forcontent-editable
el.checked = true
vs.el.setAttribute('checked', true)
el.classList
vs.el.className
is okay but whyel.dataset
and notel.dataSet
orel.dataList
?I think this article could be clearer. Firstly, it’s more helpful to refer to the DOM properties as properties rather than “attributes” to avoid any confusion. As you say, many attributes are mirrored as properties on the corresponding DOM element but they’re really quite different things and it helps to think of them that way and then it may not seem so weird. Some major differences:
Attribute values (as retrieved by
getAttribute
) are always strings. Properties can be any type (boolean, number,ClassList
etc.).Attribute and property names do not always match, for a variety of reasons. For example, JavaScript reserved words (
for
/htmlFor
) and form element states (thechecked
attribute of a check box or radio button maps to thedefaultChecked
property while the checked property has no corresponding attribute).Values may not match. URLs are a good example of this. As mentioned in a previous comment, in an
<a>
element, thehref
attribute is just the literal string in the HTML while the corresponding property will be a fully qualified URL.In general, the attribute itself isn’t usually useful in browser-based JavaScript and it’s more helpful just to deal with properties.
Not pretty sure if it makes sense to open a thread to this post, but I was wondering, how do you handle
data-
attributes in React apps? Recently I have faced some issues trying to pass somedata-
attributes like<Button data-role="foo" />
and the singles way was to handle something likeconst Button = ({
...other
}) => (
<button data-role={other['data-role']} />
);
Is there any better aproach?
classList is not an array and it does not behave like an array.
It is a DOMTokenList and the DOMTokenList.forEach function is not available in the Internet Explorer.