https://jakearchibald.com/2024/attributes-vs-properties/
Jake Archibald wrote...who?
HTML attributes vs DOM properties
Posted 24 April 2024
Attributes and properties are fundamentally different things. You can
have an attribute and property of the same name set to different
values. For example:
...
It seems like fewer and fewer developers know this, partially thanks
to frameworks:
If you do the above in a framework's templating language, you're
using attribute-like syntax, but under the hood it'll sometimes be
setting the property instead, and when it does that differs from
framework to framework. In some cases, it'll set a property and an
attribute as a side-effect, but that isn't the framework's fault.
Most of the time, these distinctions don't matter. I think it's good
that developers can have a long and happy career without caring about
the differences between properties and attributes. But, if you need
to dig down into the DOM at a lower level, it helps to know. Even if
you feel you know the difference, maybe I'll touch on a couple of
details you hadn't considered. So let's dig in...
The key differences
Before we get to the interesting stuff, let's get some of the
technical differences out of the way:
HTML serialisation
Attributes serialise to HTML, whereas properties don't:
const div = document.createElement('div');
div.setAttribute('foo', 'bar');
div.hello = 'world';
console.log(div.outerHTML); // ''
So when you're looking at the elements panel in browser developer
tools, you're only seeing attributes on elements, not properties.
Value types
In order to work in the serialised format, attribute values are
always strings, whereas properties can be any type:
const div = document.createElement('div');
const obj = { foo: 'bar' };
div.setAttribute('foo', obj);
console.log(typeof div.getAttribute('foo')); // 'string'
console.log(div.getAttribute('foo')); // '[object Object]'
div.hello = obj;
console.log(typeof div.hello); // 'object'
console.log(div.hello); // { foo: 'bar' }
Case sensitivity
Attribute names are case-insensitive, whereas property names are
case-sensitive.
However, attribute values are case-sensitive.
Ok, here's where things start to get blurry:
Reflection
Take a look at this:
This seems to contradict the first example in the post, but the above
only works because Element has an id getter & setter that 'reflects'
the id attribute.
When a property reflects an attribute, the attribute is the source of
the data. When you set the property, it's updating the attribute.
When you read from the property, it's reading the attribute.
For convenience, most specs will create a property equivalent for
every defined attribute. It didn't work in the example at the start
of the article, because foo isn't a spec-defined attribute, so there
isn't a spec-defined foo property that reflects it.
Here's the spec for . The "Content attributes" section defines
the attributes, and the "DOM interface" defines the properties. If
you click on reversed in the DOM interface, it takes you to this:
The reversed and type IDL attributes must reflect the respective
content attributes of the same name.
But not all of these reflectors are as simple as these.
Naming differences
Ok, this is relatively minor, but sometimes the property has a
different name to the attribute it reflects.
In some cases it's just to add the kind of casing you'd expect from a
property:
* On , el.crossOrigin reflects the crossorigin attribute.
* On all elements, el.ariaLabel reflects the aria-label attribute
(the aria reflectors became cross browser in late 2023. Before
that you could only use the attributes).
In some cases, names had to be changed due to old JavaScript reserved
words:
* On all elements, el.className reflects the class attribute.
* On