[HN Gopher] HTML Attributes vs. DOM Properties
       ___________________________________________________________________
        
       HTML Attributes vs. DOM Properties
        
       Author : thunderbong
       Score  : 332 points
       Date   : 2024-04-25 02:34 UTC (20 hours ago)
        
 (HTM) web link (jakearchibald.com)
 (TXT) w3m dump (jakearchibald.com)
        
       | Repulsion9513 wrote:
       | Why would you ever want to write data on an element as a property
       | instead of an attribute? Yikes.
        
         | kcrwfrd_ wrote:
         | When you don't want it coerced into a string?
        
           | berkes wrote:
           | 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 wrote:
             | For custom elements you may want to store data or methods
             | that modify the element's behavior.
        
               | berkes wrote:
               | 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 :)
        
         | rado wrote:
         | When there is no need to appear in the HTML
        
         | jraph wrote:
         | 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"?)
        
         | sir_pepe wrote:
         | When you want to pass an object to a web component without
         | going through JSON or some other stringification procedure.
        
           | politelemon wrote:
           | How do you read it in the web component afterwards? Passing
           | it via property sounds quite useful.
        
             | throwitaway1123 wrote:
             | 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 wrote:
               | The author of the thread is arguing against properties.
        
               | throwitaway1123 wrote:
               | 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 wrote:
               | Thanks understood, that makes it quite simple and
               | intuitive thinking about it.
        
           | jaffathecake wrote:
           | Event listeners? Image data?
        
             | runarberg wrote:
             | element.srcObject is very useful for <video> elements when
             | the srcObject is a direct stream feed from the camera. I
             | don't even know how I would go about passing that as a
             | string attribute.
             | 
             | https://developer.mozilla.org/en-
             | US/docs/Web/API/HTMLMediaEl...
        
         | shunia_huang wrote:
         | 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.
        
       | mg wrote:
       | 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.
        
         | lgrapenthin wrote:
         | value is a property and an attribute. Your x is only an
         | attribute.
        
         | jraph wrote:
         | 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 wrote:
           | So is "id" stored as a property or as an attribute?
        
             | justinator wrote:
             | Yes.
        
             | jraph wrote:
             | 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 wrote:
             | 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.
        
         | LegionMammal978 wrote:
         | 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...
        
       | jraph wrote:
       | 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.
        
       | vaylian wrote:
       | 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"
       | 
       | ```
        
         | klysm wrote:
         | These are also accessible to a degree from css
        
           | vaylian wrote:
           | Cool! What is the syntax for that?
        
             | domgor wrote:
             | Attributes can be used in selectors like this `div[data-my-
             | data="stuff"]`.
        
               | wildrhythms wrote:
               | 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...
        
             | LorenzA wrote:
             | attr() can also be used to some degree
             | https://developer.mozilla.org/en-US/docs/Web/CSS/attr
        
               | runarberg wrote:
               | 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">
        
         | lelanthran wrote:
         | What does `data--my-cool-attribute` supposed to become?
        
           | jaffathecake wrote:
           | The letter after - is uppercased, and the - is removed. So,
           | `div.dataset.MyCoolAttribute`.
        
             | lelanthran wrote:
             | Won't that clash in this case, with the version that has
             | double hypens?
        
               | jaffathecake wrote:
               | I'm talking about your example with double hyphens. It's
               | the initial double hyphen that causes the first letter to
               | be uppercase.
        
               | lelanthran wrote:
               | > 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 wrote:
               | el.dataset['my-CoolData'] and el.dataset.myCoolData
               | respectively
        
         | eevilspock wrote:
         | 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._
        
         | keepamovin wrote:
         | 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
        
         | SahAssar wrote:
         | 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 wrote:
           | 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.
        
             | jaffathecake wrote:
             | It is for id, but not for acronyms. Eg `innerHTML`.
             | https://w3ctag.github.io/design-principles/#casing-rules
        
               | kevincox wrote:
               | And that isn't even getting to XMLHttpRequest.
        
               | jaffathecake wrote:
               | Haha, yeah
               | 
               | - Isn't limited to XML
               | 
               | - Isn't limited to HTTP
               | 
               | - The returned object is also the response
               | 
               | Beautifully named.
        
               | bandie91 wrote:
               | just as well-thought-through as other parts of the web
               | ecosystem, starting at the scripting language designed in
               | a mere week.
        
               | schindlabua wrote:
               | I'm having to touch Spring occasionally nowadays and wow
               | it is a hodgepodge. It really is the backend equivalent
               | of jQuery.
        
               | SahAssar wrote:
               | And the first letter in the XML acronym shouldn't even be
               | X.
               | 
               | Obiously the JS API should be called
               | ExtensibleMarkupLanguageHyperTextTransportProtocolRequest
        
             | culi wrote:
             | 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...
        
           | shkkmo wrote:
           | 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-...
        
           | tyleo wrote:
           | 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 :)
        
           | meowface wrote:
           | FYI, on HN, you can get code blocks by indenting with two or
           | more spaces:                 like this
        
             | vaylian wrote:
             | Thanks. This is really good to know! Unfortunately I can no
             | longer edit my original post, but I will use your tip next
             | time.
        
         | wildrhythms wrote:
         | 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.
        
       | fregante wrote:
       | 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`)
        
       | grose wrote:
       | 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.
        
         | jaffathecake wrote:
         | 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.
        
         | shiomiru wrote:
         | > 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...
        
       | rmetzler wrote:
       | 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 wrote:
         | 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 wrote:
           | 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 wrote:
             | 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.
        
       | aixpert wrote:
       | so always use getProperty instead of getAttribute to avoid
       | surprises?
        
         | jaffathecake wrote:
         | Nope, just el.propertyName. el.getProperty isn't a thing.
        
       | clementmas wrote:
       | I understand the argument against updating the <dialog open>
       | property but it's useful to be able to target it with CSS
        
         | jaffathecake wrote:
         | That's why I said it should also have a pseudo class. Similar
         | to :checked on inputs.
        
         | Kailhus wrote:
         | 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
        
       | ale42 wrote:
       | 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.
        
       | londons_explore wrote:
       | 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.
        
         | jaffathecake wrote:
         | 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 wrote:
           | 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 wrote:
             | Yeah, that works, but it requires func() to be on the
             | global scope. It doesn't scale as a solution.
        
               | debugnik wrote:
               | Does that matter now that we've got (possibly
               | declarative) shadow roots as the units of DOM
               | encapsulation?
        
               | jaffathecake wrote:
               | Yes, even more so. You end up with encapsulated DOM
               | without encapsulated script.
        
         | skrebbel wrote:
         | 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 wrote:
           | 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.
        
             | strix_varius wrote:
             | That doesn't make any sense. DOM attributes are serialized
             | as a fundamental part of their expression.
        
               | layer8 wrote:
               | So is JavaScript. Granted, it would be more
               | straightforward if it were a Lisp.
        
             | afavour wrote:
             | 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 wrote:
               | Same is true of real javascript objects vs JSON. For
               | example, you can't put Date() in JSON. Nor objects with a
               | prototype.
        
               | afavour wrote:
               | 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.
        
               | no_wizard wrote:
               | 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...
        
         | romellem wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
               | > 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 wrote:
               | 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 wrote:
               | I think a lot of people dislike that JavaScript is a
               | general purpose programming language, rather than being a
               | scripting engine for browsers.
        
               | wredcoll wrote:
               | A lot of people dislike any tool that actually has wide
               | spread usage. No one complains about tools that have been
               | abandoned.
        
               | triceratops wrote:
               | "There are only two kinds of languages: the ones people
               | complain about and the ones nobody uses." - Bjarne
               | Stroustrop
        
             | matheusmoreira wrote:
             | 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?
        
               | layer8 wrote:
               | Mostly the latter. The web wasn't designed to become an
               | application platform.
        
               | afiori wrote:
               | 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
        
               | jaffathecake wrote:
               | That's used as a constant for eventPhase
               | https://dom.spec.whatwg.org/#ref-for-dom-event-
               | none%E2%91%A2
               | 
               | In modern DOM APIs, it would be a string enum.
        
               | afiori wrote:
               | 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
        
             | mardifoufs wrote:
             | 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.
        
         | tomjakubowski wrote:
         | 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 wrote:
         | > 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.
        
       | keepamovin wrote:
       | DOM properties also known as IDL attributes
        
         | jaffathecake wrote:
         | 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 wrote:
           | Heh, I guess I'm a stickler for the textbook. Always have
           | been hahaha :)
        
       | lloydatkinson wrote:
       | Reason #231 why web components are something of a nightmare.
        
         | strix_varius wrote:
         | 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 wrote:
           | 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 wrote:
             | 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.
        
       | amortka wrote:
       | well explained, good read.
        
       | aragonite wrote:
       | 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 wrote:
         | 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.
        
           | aragonite wrote:
           | 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.
        
           | dylan604 wrote:
           | why do you care what order the attributes are in that you
           | feel not being able to reorder them is a negative?
        
             | jaffathecake wrote:
             | I don't. I was just trying to think of anything an iterable
             | might offer over the other methods.
        
           | jgraham wrote:
           | 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 wrote:
             | 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.
        
           | paulddraper wrote:
           | Look at the DOM Attr interface [1].
           | 
           | You can list attributes, get their name, namespace, value.
           | 
           | [1] https://developer.mozilla.org/en-US/docs/Web/API/Attr
        
             | jaffathecake wrote:
             | All of which you can do with the other methods.
        
               | paulddraper wrote:
               | Sure
        
       | Waterluvian wrote:
       | 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.
        
       | notnullorvoid wrote:
       | 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 wrote:
         | 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 wrote:
           | My bad, that makes sense.
        
       | assimpleaspossi wrote:
       | Jake. Ahem: https://jakearchibald.com/2023/against-self-closing-
       | tags-in-...
        
         | jaffathecake wrote:
         | > You'll see this syntax on my blog because it's what Prettier
         | does, and I really like Prettier.
        
       | austin-cheney wrote:
       | 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.
        
         | layer8 wrote:
         | How does that difference manifest?
        
         | jaffathecake wrote:
         | 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.
        
         | cxr wrote:
         | Weird comment. There's no substantial difference in behavior
         | here (aside from the existing distinction between attributes
         | and properties).
        
       | throw156754228 wrote:
       | There's a Java script object behind each DOM node basically, that
       | has extra stuff in it.
        
       | chrisjj wrote:
       | It doesn't help that Google too is confused - and confusing.
       | 
       | Search for 'html properties' gives results mostly about
       | attributes.
        
       ___________________________________________________________________
       (page generated 2024-04-25 23:01 UTC)