return to table of content

HTML Attributes vs. DOM Properties

vaylian
29 replies
1d13h

Good article. There is also a nice middle ground with the data-attributes:

```

<div id="myDiv" data-payload="something"></div>

```

These data-attributes are then automatically available to JavaScript as a read+write property:

```

document.getElementById('myDiv').dataset.payload == "something"

```

However, one has to be mindful that HTML attributes use -kebab-case- while JavaScript uses camelCase. Thus

```

<div id="example-div" data-my-cool-data-attribute="fun"></div>

```

becomes

```

document.getElementById('example-div').dataset.myCoolDataAttribute == "fun"

```

SahAssar
13 replies
1d7h

I really dislike automatic conversion between cases. There does not seem to be any established convention of how to handle uppercase acronyms across all different ecosystems that use camelCase, so

``` document.getElementById('example-div').dataset.myID == "1" ```

becomes:

``` <div id="example-div" data-my-i-d="1"></div> ```

floydnoel
7 replies
1d6h

just treat acronyms as a word, e.g. `myId` instead of `myID`. That's been the standard practice in JavaScript as long as I've been using it at least.

kevincox
4 replies
1d6h

And that isn't even getting to XMLHttpRequest.

jaffathecake
3 replies
1d5h

Haha, yeah

- Isn't limited to XML

- Isn't limited to HTTP

- The returned object is also the response

Beautifully named.

bandie91
1 replies
1d5h

just as well-thought-through as other parts of the web ecosystem, starting at the scripting language designed in a mere week.

schindlabua
0 replies
1d4h

I'm having to touch Spring occasionally nowadays and wow it is a hodgepodge. It really is the backend equivalent of jQuery.

SahAssar
0 replies
1d4h

And the first letter in the XML acronym shouldn't even be X.

Obiously the JS API should be called ExtensibleMarkupLanguageHyperTextTransportProtocolRequest

culi
0 replies
1d3h

That is the standard practice in Java. But JavaScript has XMLHttpRequest, innerHTML, etc

There's not really a good standard at all, but I think the _emerging_ practice is captured well here

When using acronyms, use Pascal case or camel case for acronyms more than two characters long. For example, use HtmlButton or htmlButton . However, you should capitalize acronyms that consist of only two characters, such as System.IO instead of System.Io . Do not use abbreviations in identifiers or parameter names.

https://learn.microsoft.com/en-us/previous-versions/dotnet/n...

tyleo
1 replies
1d5h

Personal pet peeve but IMO ‘ID’ is a bad example because it is an abbreviation, not an acronym. I generally recommend ‘Id’ but I’ll admit, it’s like a tabs/spaces debate.

‘UI’ may be a better example :)

Andrex
0 replies
19h13m

Whoah, I never thought about that. You're totally right, "ID" should really be "Id."

meowface
1 replies
1d3h

FYI, on HN, you can get code blocks by indenting with two or more spaces:

  like this

vaylian
0 replies
1d3h

Thanks. This is really good to know! Unfortunately I can no longer edit my original post, but I will use your tip next time.

shkkmo
0 replies
1d6h

It isn't hard to correctly convert from camel case, you just tree any sequence of one letter words as a single word. However, without a standard for camel case acronym calculation, when you convert back you won't necessarily get an exact match.

I presume, the ability to get the exact camelCase capitalization you want is why the html spec does the conversion this way: https://html.spec.whatwg.org/multipage/dom.html#dom-dataset-...

lelanthran
5 replies
1d9h

What does `data--my-cool-attribute` supposed to become?

jaffathecake
4 replies
1d9h

The letter after - is uppercased, and the - is removed. So, `div.dataset.MyCoolAttribute`.

lelanthran
3 replies
1d6h

Won't that clash in this case, with the version that has double hypens?

jaffathecake
2 replies
1d6h

I'm talking about your example with double hyphens. It's the initial double hyphen that causes the first letter to be uppercase.

lelanthran
1 replies
1d6h

I'm talking about your example with double hyphens. It's the initial double hyphen that causes the first letter to be uppercase.

I was asking about the initial example, and made a mistake in the question I asked.

To clarify, according to the standards, what do the following transformations result in?

    data-my--cool-data
    data-my-cool-data

jaffathecake
0 replies
1d5h

el.dataset['my-CoolData'] and el.dataset.myCoolData respectively

klysm
5 replies
1d12h

These are also accessible to a degree from css

vaylian
4 replies
1d12h

Cool! What is the syntax for that?

domgor
1 replies
1d11h

Attributes can be used in selectors like this `div[data-my-data="stuff"]`.

wildrhythms
0 replies
21h38m

CSS is very powerful here because you can take advantage of the other attribute selectors to find partial matches:

div[data-my-data^="my-prefix"] (selects by prefix)

div[data-my-data$="my-suffix"] (selects by suffix)

div[data-my-data*="my-substring"] (selects by substring)

Quite a few more of these:

https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_s...

runarberg
0 replies
1d3h

The attr() function is actually much more powerful in the spec (perhaps one of my favorite part of unimplemented CSS). According to the spec you can supply the type of the function and use it as value (not just content as of now):

    <div data-background="lime">Red (but could be lime)</div>

    div {
      background: red;
    }

    div[data-background] {
      background: attr(data-background color, red);
    }
So according to the spec, you should be able to control the sizes, color, and even animation timings with (non-style) attributes.

In reality though, whenever I think I need this advanced attr() function, I usually just solve the issue using Custom Properties on the `style` attribute:

   <div style="--background: lime">

wildrhythms
0 replies
21h43m

I avoid all of the kebab/camel conversions by using the methods:

Element.hasAttribute('data-thing')

Element.getAttribute('data-thing')

Element.setAttribute('data-thing', '...')

Much more clear what I'm doing without the awkward translating when I inevitably have to inspect/debug in the DOM.

keepamovin
0 replies
1d8h

It’s an interesting and another way to communicate across JavaScript contexts.

By contexts here I’m not specifically talking about different Iframes — then it would be beholden to the same origin restrictions and probably have to use postmessage - but I mean in the context of a browser extension, where you create a parallel JavaScript execution context that has access to the same DOM

These are also called isolated worlds in the language of the dev tools protocol

eevilspock
0 replies
1d8h

MDN article Using data attributes describing HTML syntax, JavaScript access and CSS access, and a caveat about accessibility: https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Us...

Also https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att..., which says:

> The data-* global attributes form a class of attributes called custom data attributes, that allow proprietary information to be exchanged between the HTML and its DOM representation by scripts.

londons_explore
29 replies
1d9h

And all of the things in this article represent complexity and behaviours that don't add anything to anyone's lives.

This behaviour is all downside and no upside.

The spec should have simply defined a dom object and a javascript object to be one and the same, with attributes and properties being the same thing.

romellem
13 replies
1d7h

Most modern UI frameworks wouldn’t work at all if this limitation “simply” existed. Being able to store non-string values on DOM nodes is an upside.

Acting this flippant isn’t helpful.

ezst
12 replies
1d6h

To my non-webdev ears this sounds like "having only pegs and square holes isn't a problem because otherwise how would you try to fit one in the other?"

Could it be that this whole paradigm (which by the way has been devised for a different and much simpler use case, and abused forever since) isn't sustainable anymore?

jacobsimon
5 replies
1d5h

I think the article missed an important “why” here that is confusing people here: JavaScript objects and the DOM are fundamentally different, and JavaScript is providing an API via the DOM for reading and writing HTML, which is essentially an XML document. We shouldn’t expect the objects in JavaScript to perfectly translate to their HTML counterparts or vice versa.

If anything, it’s odd that they added these conveniences like reflection that make it more magical than it should be.

(Edited for clarity)

naasking
4 replies
1d4h

We shouldn’t expect the objects in JavaScript to perfectly translate to their DOM counterparts or vice versa.

Since JavaScript was invented specifically to manipulate the DOM, I'm not sure that that follows.

jacobsimon
3 replies
1d4h

That's definitely one of the main original use cases, but it would have been a bad decision to base the entire language around the limitations of HTML, and I doubt JavaScript today would be the most popular programming language today if they had. You can read the original ECMAScript 1 standard here, which is titled: "A general purpose, cross-platform programming language"[1].

https://ecma-international.org/wp-content/uploads/ECMA-262_1...

AmpsterMan
2 replies
1d1h

I think a lot of people dislike that JavaScript is a general purpose programming language, rather than being a scripting engine for browsers.

wredcoll
1 replies
1d

A lot of people dislike any tool that actually has wide spread usage. No one complains about tools that have been abandoned.

triceratops
0 replies
1d

"There are only two kinds of languages: the ones people complain about and the ones nobody uses." - Bjarne Stroustrop

matheusmoreira
4 replies
1d4h

I wonder why the web technologies are not fully integrated like the Smalltalk and Self graphical environments... Is it impossible or did it just end up that way and it's too late to change it now?

afiori
2 replies
1d2h

The DOM API is not designed as a JavaScript API (at least originally). It was designed to be language independent and to be more natural for a language like Java rather than JavaScript.

The reason for NodeList instead of Array is the same

For example the Event interface definition[0] contains parts like

  const unsigned short NONE = 0;
[0] https://dom.spec.whatwg.org/#interface-event

afiori
0 replies
1d

I did not mean to disparage the DOM API, I intended to show that the DOM API was not (initially) designed as a Javascript API and was instead expressed in a language independent (but Java inspired) IDL.

I would not surprise me if modern addition or revisions to the spec were more JS inspired

layer8
0 replies
1d3h

Mostly the latter. The web wasn’t designed to become an application platform.

mardifoufs
0 replies
1d1h

What do you mean by not sustainable? What has been more sustainable than web dev and web tech lol? Also, being flexible isn't trying to fit a square peg in a hole, it's the opposite actually.

skrebbel
7 replies
1d9h

I agree, except with the “simply” part. This is not simple at all, unless you also demand that every property can only ever be a string. Otherwise, you need to think of conversions (eg from and to numbers), and what about properties that can be set to objects? Or properies that don’t correspond to attributes at all?

I agree that any sort of convention/rule here that’d be 100% fixed would’ve been better, but it wouldn’t be simple.

londons_explore
6 replies
1d8h

think the other way round... allow DOM attribute values to be of any javascript type...

Sure, they maybe can't be serialized, but that isn't an issue.

afavour
3 replies
1d6h

In what way is that not an issue? What happens when you use .outerHTML to get a string representation of the element? Not to mention that, unavoidably, the entire page HTML arrives to the browser as a string.

londons_explore
2 replies
1d5h

Same is true of real javascript objects vs JSON. For example, you can't put Date() in JSON. Nor objects with a prototype.

no_wizard
0 replies
1d3h

I do want to point out for those who don't know, on objects with a prototype (such as a class) you can control how JSON.stringify works by adding a special method `toJSON`[0] which will control how that object is serialized.

[0]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

afavour
0 replies
1d4h

Right… and that’s an issue people have to work around every day. Non string values in HTML world similarly be an issue everyone would have to work around.

strix_varius
1 replies
1d7h

That doesn't make any sense. DOM attributes are serialized as a fundamental part of their expression.

layer8
0 replies
1d3h

So is JavaScript. Granted, it would be more straightforward if it were a Lisp.

jaffathecake
4 replies
1d9h

This means you couldn't non-string values on DOM interfaces, and that includes methods, so most of the DOM's functionality goes out the window.

noam_k
3 replies
1d9h

Good point, but HTML already has a workaround by using strings:

<div onClick="func()">...</div>

Now, I'm not a JS developer, so my opinion shouldn't count for much, but that looks like a much less usable standard to me.

jaffathecake
2 replies
1d9h

Yeah, that works, but it requires func() to be on the global scope. It doesn't scale as a solution.

debugnik
1 replies
1d8h

Does that matter now that we've got (possibly declarative) shadow roots as the units of DOM encapsulation?

jaffathecake
0 replies
1d8h

Yes, even more so. You end up with encapsulated DOM without encapsulated script.

tomjakubowski
0 replies
1d5h

Making attributes and properties symmetric to one another would be difficult for the existing DOM API: methods like `addEventListener` are (prototype) properties on individual DOM elements, but would be nonsensical string attributes. Making your idea concrete:

    // our document is like:
    // <foo bar="asdf">
    let foo = document.querySelector('foo'); 
    console.log('bar', foo.bar); // cool, prints asdf
    foo.bar = 'nice'; // <foo> element is now <foo bar="nice">
    foo.addEventListener('some-event', e => console.log(e)); // wait, what?
So DOM element methods would instead be free functions like:

    HTMLElement.addEventListener(foo, 'some-event', e => console.log(e));
It's a matter of taste, but I for one appreciate having object method calls in JavaScript.

chrisjj
0 replies
1d1h

all of the things in this article represent complexity and behaviours that don't add anything to anyone's lives.

A cynic might point out it added a lot of makework to the lives of its creators. And that this is why we can't have nice things like proper specs instead of 'living' ones.

Repulsion9513
15 replies
1d13h

Why would you ever want to write data on an element as a property instead of an attribute? Yikes.

sir_pepe
7 replies
1d12h

When you want to pass an object to a web component without going through JSON or some other stringification procedure.

politelemon
4 replies
1d11h

How do you read it in the web component afterwards? Passing it via property sounds quite useful.

throwitaway1123
3 replies
1d9h

Web components are implemented using custom elements which are just plain JS classes that extend the HTMLElement class, so you would access the property the same way you would access a property in a normal JS class.

jaffathecake
1 replies
1d9h

The author of the thread is arguing against properties.

throwitaway1123
0 replies
1d9h

Yes, I know. I wasn't making a prescriptive comment about whether or not developers should use properties, I was just making a descriptive comment about how properties are accessed in a web component to answer the specific question politelemon asked.

politelemon
0 replies
1d1h

Thanks understood, that makes it quite simple and intuitive thinking about it.

jaffathecake
1 replies
1d10h

Event listeners? Image data?

kcrwfrd_
3 replies
1d13h

When you don’t want it coerced into a string?

berkes
2 replies
1d8h

For primitive types, serde isn't hard. It could even be part of a simple spec. Numbers, Dates, String[], Number[], Date[]. Edit: Or it could just be JSON.

For complex types it is. But why and when do you need to store complex types in the DOM? Isn't that always a bad idea?

I'm a seasoned web dev, but haven't been working on complex frontend apps, because I believe complexity and frontend web don't go together. So I may very well miss some use-cases.

bryancoxwell
1 replies
1d7h

For custom elements you may want to store data or methods that modify the element’s behavior.

berkes
0 replies
1d3h

Methods as in callbacks? So, code? Do you then store a closure or such? Or a pointer to one (i.e. the function/name)? And how or what data would modify behaviour?

As said: I'm unfamiliar with these concepts as I actively try to avoid them :)

shunia_huang
0 replies
1d12h

I believe it's quite useful for framework developers for not required to build an abstract layer upon the already-exist-and-well-designed DOM layer to manage the state transitions or event handlings or something similar, you can't do too much if you are constrained to use only string values to encode/decode the inner state/data on the DOM.

rado
0 replies
1d13h

When there is no need to appear in the HTML

jraph
0 replies
1d13h

Your sentence can be understood in two ways, I'll answer the question "Why use domElement.attrname instead of the corresponding domElement.setAttribute('attrname') and domElement.getAttribute('attrname')?".

It can look cleaner (matter of taste). It looks like a native JS assignment, and it's shorter.

For open and hidden, it's way more intuitive and convenient to set and get booleans than testing the existence of the corresponding HTML attribute (Still not a fan of this thing, years after having learned this).

(but maybe you meant "Why use domElement.randomprop instead of something like domElement.dataset.randomprop"?)

aragonite
9 replies
1d6h

What helps me make sense of it is that attributes in the narrowest sense are DOM nodes (Attr nodes). They can have properties (such as `name`, `value`, `ownerElement`, `prefix`), and collectively (as a NamedNodeMap, not an array) they can be accessed as the value of the `attributes` property of the element that owns them.

So in a sense the name of getAttribute(name) is misleading: it's probably better renamed getAttributeValue(name), because it doesn't really return an attribute (in the sense of an Attr node), but the value of the `value` property of the attribute node (owned by the element on which it is called) whose name is `name`.

See:

https://developer.mozilla.org/en-US/docs/Web/API/Element/get...

https://developer.mozilla.org/en-US/docs/Web/API/Element/get...

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

jaffathecake
8 replies
1d6h

That's all true, but is there anything practical you can do with an attribute node, and el.attributes, that you can't do with setAttribute/getAttribute/getAttributeNames/hasAttribute?

You can't even move attribute nodes between elements. You can't event reorder attribute nodes within an element.

jaffathecake
1 replies
22h45m

All of which you can do with the other methods.

paulddraper
0 replies
19h54m

Sure

jgraham
1 replies
1d1h

Work with attributes whose name doesn't match the XML Name production.

That can be a real concern in some cases (it came up recently in the context of "how do I move all the attributes on node A to node B, given that node A's attributes can be anything that can be created by the HTML parser?" c.f. https://software.hixie.ch/utilities/js/live-dom-viewer/?save... )

jaffathecake
0 replies
1d1h

Hah, good insight! I didn't realise you could move attributes either. I tried, but seems (unlike nodes) you need to remove them before adding them.

dylan604
1 replies
1d4h

why do you care what order the attributes are in that you feel not being able to reorder them is a negative?

jaffathecake
0 replies
22h46m

I don't. I was just trying to think of anything an iterable might offer over the other methods.

aragonite
0 replies
1d6h

Not that I know of! It's just a way to help myself keep the distinction straight mentally. If at the back of my head I always think of attributes as DOM nodes in their own right, I'm much less tempted to infer from a DOM element having a particular attribute to it possessing a particular property, and vice versa.

austin-cheney
8 replies
1d3h

Perhaps the most important difference is persistence, and isn’t covered by the article.

Attributes are persistent for the duration that the same root object of the given DOM tree remains available for access, such as not leaving the page.

Object properties only remain available until the given object is garbage collected.

jaffathecake
3 replies
1d2h

The object exists as long as the DOM node (the thing with the attributes) exists, so the lifecycle is the same.

The DOM node could be cloned, or go through serialisation and deserialisation, in which case it's now a completely different DOM node (although with the same attributes), so it won't have the non-default properties of the other node.

austin-cheney
2 replies
14h47m

No. That is incorrect and makes assumptions not present.

The DOM is a language agnostic tree model in memory. The DOM, or any node therein, is accessed via the DOM’s API. Accessing a single node in JavaScript generates a node object. That node object is an artifact in JavaScript language representing the DOM node at the moment of access.

A DOM node is a living mutable thing, but the JavaScript object representing that node is not. It’s a static object in JavaScript language. That is also why a node list is not an array.

The DOM is not an artifact of JavaScript. I can understand how this is confusing if you have never operated without a framework, but otherwise it’s really straightforward.

This is one of the reasons I abandoned my JavaScript career. Nobody knows what the DOM is, which is their only compile target, and yet everyone wants to an expert.

jaffathecake
1 replies
12h9m

What I said in my previous comment is observably true. Try making a demo where it isn't.

A DOM node is a living mutable thing, but the JavaScript object representing that node is not.

The JavaScript object is mutable. The first example in the article shows this.

That is also why a node list is not an array.

Modern APIs on the web return platform arrays (eg JavaScript arrays). https://webidl.spec.whatwg.org/#js-sequence - here's where the WebIDL spec specifies how to convert a sequence to a JavaScript array.

I'm fully aware of NodeList. There's a reason the spec calls them "old-style" https://dom.spec.whatwg.org/#old-style-collections

I can understand how this is confusing if you have never operated without a framework, but otherwise it’s really straightforward

Sighhhhhh. I've been a web developer for over 20 years, and spent a decade on the Chrome team working on web platform features. Most of my career has been on the low-level parts of the platform.

Could it be possible that people are disagreeing with you, not because they're stupid, but because you're in the wrong? Please try to be open minded. Try creating some demos that test your opinions.

austin-cheney
0 replies
7h33m

The JavaScript object will be garbage collected as seen fit by the corresponding JavaScript run time irrespective of whether the associated node remains attached to its document.

That is challenging to observe when using event listeners, as opposed to assigning events directly to handlers, because listeners interfere with garbage collection. Furthermore, this almost impossible to observe when abstracted by large frameworks.

As for a demo I am currently out of town without a computer, but you can use this project to perform experimental qualifiers: https://github.com/prettydiff/share-file-systems

That project forms an OS like GUI in the browser without listeners and eliminates most non-locally scoped DOM node references in the event handlers. This allows for localized event handlers with localized DOM node references that are always ready for garbage collection. This also allows state restoration of greater than 10,000 DOM nodes without a large increase in load time and without increased memory consumption. So instead of normal page load with state restoration of about 80ms blowing up to 10,000 nodes could take up to 300+ms. If you want to achieve extreme performance, in any language, you must absolutely understand your compile target. The compile target of the browser is the DOM.

You could also try it on my website which has far less functionality but allows rapid experimentation of the state management. http://prettydiff.com

cxr
2 replies
1d2h

Weird comment. There's no substantial difference in behavior here (aside from the existing distinction between attributes and properties).

austin-cheney
1 replies
14h45m

The DOM has nothing to do with JavaScript. I explained this in further detail to a peer comment.

cxr
0 replies
14h11m

The DOM is not JavaScript. It doesn't make the original comment any less weird. (The reply with further detail that you referred to is even weirder.)

layer8
0 replies
1d3h

How does that difference manifest?

mg
7 replies
1d13h

The gist of it seems to be that attributes sometimes behave like properties.

Setting 3 attributes on an html element:

    <input id=good x=morning value=hn>
Then logging 3 properties with the same names:

    <script>
        e = document.querySelector('input');
        console.log(e.id); 
        console.log(e.x);
        console.log(e.value);
    </script>
Will output

    good
    undefined
    hn
I'm not sure why. The article says it's because "Element has an id getter & setter that 'reflects' the id attribute". Which sounds logical. But MDN does not mention a getter function for "value" for example:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/in...

It even calls "value" a property AND an attribute: "...access the respective HTMLInputElement object's value property. The value attribute is always..."

And HTMLInputElement lists "value" under "properties":

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

So maybe it's not as easy. Maybe <element some=thing> sometimes sets a property and sometimes sets an attribute.

jraph
4 replies
1d12h

It's a matter of getters and setters being defined for well-known attributes on the DOM element indeed. This is why it doesn't work of 'x', the 'x' HTML attribute doesn't have a getter and a setter defined on the 'x' property of the DOM element (You can try defining custom getters and setters with defineProperty that call getAttribute and setAttribute if you want to experiment - I haven't experimented but I expect this to work and I think that's how predefined properties work).

For 'value', MDN says:

it can be altered or retrieved at any time using JavaScript to access the respective HTMLInputElement object's value property

While it doesn't say it is implemented using a setter and a getter (I suspect because it doesn't need to go into such details that are confusing if you don't know this mechanism), I think this is what it implies. I believe this can be found in the spec of the Javascript implementation of DOM.

So it is as "easy" as it looks (note my phrasing totally allows you to find it looks difficult): for well-known HTML attributes, there are usually getters and setters for the corresponding DOM property. Usually, it's the same string, sometimes there's a conversion, for instance with booleans (for the open and the hidden attributes).

mg
3 replies
1d12h

So is "id" stored as a property or as an attribute?

justinator
0 replies
1d12h

Yes.

jraph
0 replies
1d12h

It might depends on the implementation, and I've haven't dug into actual implementations but you pretty much need an attribute mapping (from names to values) for many things and that's how I would store it.

There might be additional optimizations or storage features for certain attributes. For example for id, since you mention this attribute specifically, you pretty much want document.getElementById() to be fast, you probably don't want it to traverse the whole DOM tree each time is called, so there's likely an additional mapping from ids to DOM elements stored somewhere per document, that is to update each time the id attribute is changed (though the spec [1] does appear to say anything about the speed characteristics of getElementById; in practice I personally certainly assume it's quasi instantaneous when using it).

For other attributes, more generally, you need to store the attribute value, not the property value (which you can cache, though), because the property value can be coerced. For instance, hidden="hidden" or hidden="HIDDEN" both lead to .hidden == true. But you need the exact value for getAttribute or for HTML serialization.

[1] https://dom.spec.whatwg.org/#ref-for-dom-nonelementparentnod...

jaffathecake
0 replies
1d10h

An attribute. From https://jakearchibald.com/2024/attributes-vs-properties/#ref...

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.

value is different. From https://jakearchibald.com/2024/attributes-vs-properties/#val...

the value property does not reflect the value attribute. Instead, the defaultValue property reflects the value attribute.

Follow the link for the full explanation.

lgrapenthin
0 replies
1d12h

value is a property and an attribute. Your x is only an attribute.

LegionMammal978
0 replies
1d12h

There are two different objects here. In HTML, you have an <input> element with a "value" attribute [0]. An <input> element also has an internal value, which is initialized using the "value" attribute.

In JavaScript, you have an HTMLInputElement with a "value" property [1]. Getting the "value" property of an HTMLInputElement reads from its <input> element's internal value, and setting the "value" property of an HTMLInputElement writes to its <input> element's internal value. (The "value" attribute remains unchanged, since it is only used for initialization.) The DOM object is just modeling the actual element.

In general, the property on the DOM object will not exist unless it is specifically documented to, such as "id" and "value".

[0] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/in...

[1] https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputEl...

rmetzler
3 replies
1d12h

LOL, I feel like the author mix up the two things even in the title. There are HTML attributes, there is the DOM API (which for example exists also in other languages, but not always refers to HTML but mostly XML instead) and there are JavaScript Object properties. And because the DOM api uses JavaScript objects you can access properties. But only attributes are serialized / deserialised. And some frameworks blur this, so you get and set both as a „convenience“.

jaffathecake
2 replies
1d10h

I know they're two different things. I wouldn't bother writing an article comparing two things that are the same, even though it wouldn't take long.

rmetzler
1 replies
1d7h

Apparently I worded my first sentence wrong and you didn't bother to read longer than that. Apologies.

I just want to reiterate: DOM objects do have properties, because they are JavaScript objects. You make it sound like they have properties because this is something the DOM API set. And unfortunately this is only the case for some special HTML attributes, not for all of them.

jaffathecake
0 replies
1d6h

I think you missed a couple of bits in the article:

the above only works because Element has an id getter & setter that 'reflects' the id 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.

This is where the distinction is made between merely setting a property on a JavaScript object, and cases where you're actually calling an HTML-spec'd getter that has side effects.

The whole "reflection" section of the article is dedicated to how these HTML-spec'd getters and setters change the behaviour from the basic JavaScript property operations shown at the start of the article, and how it differs from property to property.

"LOL", I suppose.

lloydatkinson
3 replies
1d7h

Reason #231 why web components are something of a nightmare.

strix_varius
2 replies
1d7h

This has nothing to do with web components. This is the behavior of built in HTML elements:

https://developer.mozilla.org/en-US/docs/Glossary/IDL#conten...

If anything, web components make this behavior easier to control. They represent the ground truth rather than providing a leaky abstraction. The article demonstrates this in several places.

kaoD
1 replies
1d7h

Well it has something to do: this complexity comes up front-and-center when writing Web Components (or rather, custom elements), where you often would want to pass around JS values seamlessly like in e.g. React props.

Since custom elements are still in the regular DOM you have to deal with this soon enough that writing a custom element is likely the time you'd learn about this.

And then you have to write many lines to deal with the impedance mismatch while you wouldn't in any component framework, including ensuring that both are synced in whatever form of serialization you come up with -- which ends up being an even greater mess usually... not to mention you also pay the de/serialization costs if you want to keep a real sync between them.

As you can see I have written more `attributeChangedCallback`s and property proxies than I'd like (which is zero).

lloydatkinson
0 replies
1d1h

Thank you for writing this, it was frustrating reading their reply stating "this has nothing to do with web components" as if I was just making things up for fun.

notnullorvoid
2 replies
1d5h

Nice article, makes me wonder what a better representation would look like. It would be interesting if Html could have non string attributes that reference some local values from a scoped script tag or something.

I found the take on attributes for default configuration to be an odd take. Think about "class" attribute for example. If it only ever showed the default, toggling classes would look really odd in the dom inspector / stringified html, either not showing an active class or showing an inactive one.

jaffathecake
1 replies
1d4h

I didn't say it was for default configuration, just configuration. The class attribute works within this system. I, as the owner of the document, configure the class attribute. I can change its configuration over time, but only I do that.

An alternative system would be where the browser adds and removes classes of its own, in response to things like hover/focus state. Thankfully it doesn't, and pseudo-classes are used instead.

notnullorvoid
0 replies
1d1h

My bad, that makes sense.

keepamovin
2 replies
1d8h

DOM properties also known as IDL attributes

jaffathecake
1 replies
1d8h

Yeah, I deliberately avoided that terminology because few people outside of spec authors call them IDL attributes, and calling both things attributes is very confusing.

keepamovin
0 replies
1d7h

Heh, I guess I’m a stickler for the textbook. Always have been hahaha :)

grose
2 replies
1d12h

Great article. Maybe a hot take but I think it's a good thing that dialog and details modify the DOM. It allows for UI state to be serialized and stored in a native way. I think input.value should work the same way too (although it's probably too late for that). Consider how some browsers will remember what you input into forms when you hit the back button: I think representing this as a cached version of the modified DOM makes a lot more sense than whatever magic browsers currently use to remember it. Many CSS pseudoclasses like :checked feel like a pre-attribute-selector bandaid.

shiomiru
0 replies
1d10h

Consider how some browsers will remember what you input into forms when you hit the back button: I think representing this as a cached version of the modified DOM makes a lot more sense than whatever magic browsers currently use to remember it.

That would not work either:

A control's value is its internal state. As such, it might not match the user's current input.

For instance, if a user enters the word "three" into a numeric field that expects digits, the user's input would be the string "three" but the control's value would remain unchanged. Or, if a user enters the email address " awesome@example.com" (with leading whitespace) into an email field, the user's input would be the string " awesome@example.com" but the browser's UI for email fields might translate that into a value of "awesome@example.com" (without the leading whitespace).

From the standard: https://html.spec.whatwg.org/multipage/form-control-infrastr...

jaffathecake
0 replies
1d10h

Yeah, I get your point. On the other hand, I prefer the serialisation of the light DOM to reflect only changes I've made as the author.

The serialisation isn't expected to represent page state. Imagine how that would work with <canvas>, or even things like event listeners.

clementmas
2 replies
1d12h

I understand the argument against updating the <dialog open> property but it's useful to be able to target it with CSS

jaffathecake
0 replies
1d11h

That's why I said it should also have a pseudo class. Similar to :checked on inputs.

Kailhus
0 replies
1d

That’s my case across all others too, so much eassier to debut by checking the dom in an instant rather go through the debugger process

jaffathecake
0 replies
1d4h

You'll see this syntax on my blog because it's what Prettier does, and I really like Prettier.
aixpert
1 replies
1d12h

so always use getProperty instead of getAttribute to avoid surprises?

jaffathecake
0 replies
1d10h

Nope, just el.propertyName. el.getProperty isn't a thing.

throw156754228
0 replies
1d3h

There's a Java script object behind each DOM node basically, that has extra stuff in it.

jraph
0 replies
1d13h

Interesting article. I had built the right intuitions empirically (except for frameworks, which I don't practice much) but it's nice to read it.

The funny thing is, React popularised using className instead of class in what looks like an attribute. But, even though you're using the property name rather than the attribute name, React will set the class attribute under the hood.

Yeah, I never understood why they did this. className in their XML-like syntax is ugly and it doesn't seem like they needed to do this. They could have gone with class. They are parsing this with their custom JSX parser, and these things are not JS identifiers. IIRC you need to use {accolades} to put JS expressions (objects) there.

fregante
0 replies
1d12h

This is a good read for JS developers.

TL;DR: they’re not the same thing. Only sometimes they match (e.g. `id`) but often they don’t.

Something fun: JSX does not use attributes, even if they look like it. That’s why they have unusual naming/formatting (`htmlFor` and `className`)

chrisjj
0 replies
1d1h

It doesn't help that Google too is confused - and confusing.

Search for 'html properties' gives results mostly about attributes.

amortka
0 replies
1d7h

well explained, good read.

ale42
0 replies
1d10h

Nice to see this well explained with examples, it can for sure benefit many people, especially that there are several details that are sometimes overlooked.

Waterluvian
0 replies
1d5h

Fascinating article. I had always perceived DOM elements in JavaScript as proxies. They let you do things to that DOM element, but it’s still just a javascript object that you can assign to and do whatever, in addition to using the functions for interacting with the actual element.