16

It's said by this article that one of the important reasons for HTML properties to be reflected back to the DOM is because CSS selectors rely on attributes, but why's that? This could be done without the reflection based on the spec.

For people who don't know what I'm talking about, read below:

In browsers, CSS selectors rely on attributes to work.

#myButton[someAttribute] {
  opacity: 0.5; 
  font-weight: bold
}

So in our JavaScript if we change the property of an element, eventually we have to reflect it to the HTML DOM as well like this:

// we have changed some property
myButton.someAttribute= true; 

// but this is not adequate, we need to reflect as well
myButton.setAttribute('someAttribute', '');

so we get this:

<button id="myButton" someAttribute></button>

not this non-reflected button:

<button id="myButton"></button>
TylerH
  • 20,799
  • 66
  • 75
  • 101
Mehdi Raash
  • 8,721
  • 2
  • 29
  • 42
  • 1
    HTML attributes are used as the initial values of DOM properties. – Barmar Aug 02 '17 at 22:06
  • If I understand what you're asking, the answer is because the DOM is everything. If it's not in the DOM it's nebulous, that is, it's nowhere to be used by the browser to display the document. – Rob Aug 02 '17 at 22:12
  • Yes agreed. 'the Dom is everything', so if it's that, Why do we need to reflect all the time, for example here CSS selectors proper working was our intend? If we do reflect, our code would be more declarative, I know that, but Does it worth? – Mehdi Raash Aug 02 '17 at 22:14
  • or... you know.... just don't use attribute selectors. i mean, – Kevin B Aug 02 '17 at 22:14
  • 1
    Closing the question, are you trying to cleanse the community or helping? – Mehdi Raash Aug 02 '17 at 22:16
  • 1
    Can it be both? – Kevin B Aug 02 '17 at 22:18
  • It can't :-), I guess. – Mehdi Raash Aug 02 '17 at 22:18
  • Your citation is incredibly confusing. The link points to Google, then you mention the "spec", but the quote following *that* is from Mozilla (and not even attributed correctly so I had to remove it). – BoltClock Aug 03 '17 at 05:24

7 Answers7

9

Not all DOM properties map to attributes. The ones that do reflect to and from attributes, do so to maintain parity with the document language — in this case, HTML, which only has a concept of attributes, which as you've correctly pointed out is relied on by Selectors.

If attribute selectors mapped directly to DOM properties without the DOM discriminating between attribute properties and other kinds of properties, then attribute selectors such as the following would match, even though none of these exist as attributes in HTML:

[classList]
[className]
[dataset]
[offsetLeft]
[offsetTop]
[offsetWidth]
[offsetHeight]

... as well as [someAttribute] matching elements with your non-existent someAttribute as a property even when you don't reflect it with setAttribute().

In fact, this is exactly why label[htmlFor] incorrectly matches label[for] elements in Internet Explorer 7, even though the for attribute in HTML is simply called for, not htmlFor — the DOM uses htmlFor to make up for the fact that for is a reserved word in many languages including JavaScript, the main DOM scripting language, preventing it from being used as a property ident.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Great! so we can conclude that it's because of not making mistake (engine and even the developer ) and somehow filtering the logic. – Mehdi Raash Aug 03 '17 at 09:59
7

DOM attributes and properties are not equivalent, but they're related.

Attributes are intended to be used to initialize DOM properties. When the HTML is parsed, all the attributes are used to initialize the corresponding DOM properties. If you later modify an attribute with setAttribute or removeAttribute, the corresponding property is also updated (similar to reloading the HTML with the new attribute).

But it doesn't go the other way. Updating a property doesn't change the corresponding attribute. This is why you can assign to the .value of an input, and see this reflected in the browser display, but when you look at the element in Developer Tools you still see the original value="whatever" attribute. In some cases this has special benefits -- when you click on the Reset button of a form, it resets all the value properties from their value attributes.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks for the answer, firstly. `But it doesn't go the other way` Why's that? Shouldn't be these two in sync? – Mehdi Raash Aug 02 '17 at 22:26
  • 1
    No, because attributes are the initial values of properties, not the current values. Attributes are like the HTML source, properties are the result of parsing. – Barmar Aug 02 '17 at 22:29
  • Yes, but what problem would be brought up if JS changed the initial values( modifying the current state of HTML source)? – Mehdi Raash Aug 02 '17 at 22:35
  • 1
    Reset buttons wouldn't work any more. When you change the value of an input, it would also change the attribute, and then it won't be able to restore them to their original values. – Barmar Aug 02 '17 at 22:40
  • 3
    I think it's more accurate to say that mutating the DOM tree doesn't change the source markup - since the source markup is part of the HTTP response and cannot be altered in any way. It does, however, change the HTML representation *of the DOM tree*. So in fact you're really dealing with two "HTML" things here - the source markup that is parsed to build the initial tree, and the markup representation of the tree after the fact, which is built on-the-fly. The former stops being relevant *as soon as the page is done loading*, and the latter is what you see in developer tools. – BoltClock Aug 03 '17 at 04:44
  • @BoltClock That's why I said it's *like* the source. They similarity is that the mapping only goes one way. – Barmar Aug 03 '17 at 18:10
  • _"Updating a property doesn't change the corresponding attribute."_ To what extent do you mean? `document.body.dataset.abc = 123` will set the `data-abc="123"` attribute at `` HTML – guest271314 Aug 07 '17 at 00:42
  • 1
    @guest271314 See BoltClock's answer, it corrects this inaccuracy in my answer. – Barmar Aug 07 '17 at 14:21
  • 1
    @guest271314 And see Alohci's comments below spanky's anser. – Barmar Aug 07 '17 at 14:22
6

IMHO; Some attributes have a 1:1 mapping with their respective properties in the DOM. The reflection is automatically made for common attributes like id. You can also define your own attributes (your HTML will be considered invalid, but you can use the doctype to validate them). My guess is that due to this uncertainty created by rogue attributes. They preferred to map only the attribute:property which has predictable behaviour and usage. You can still use your custom attributes in your css but you're in manual mode. You got to keep your s**t together and reflect them yourself. This far west(freedom) mentality is one the things that made web tech so popular and easy to use. You can do things as you see fit. I do not recommend it for maintainability reasons but you could.

amenzou
  • 319
  • 1
  • 7
5

Your example uses a button, but the article is using the disabled property but with something other than a button. On a button, the browser will automatically reflect changes to the disabled property onto the attribute, and vice versa, but this doesn't happen with all elements. Change your example to use a div and you'll see that you'd need to manually coordinate the two if desired.

Or for custom attributes, use data- attributes instead. If you delete the property from my_element.dataset, I'm pretty sure the attribute will be deleted too.

spanky
  • 2,768
  • 8
  • 9
  • His question is not specific to any particular property, it's about the relationship between attributes and properties. – Barmar Aug 02 '17 at 22:29
  • @Barmar: His example was specific and so the point of the answer is that you some property changes will be reflected and some will not, depending on the element and property. – spanky Aug 03 '17 at 20:24
  • He originally wrote the question with the specific attribute 'disabled', then edited to use a generic attribute. That was about 10 minutes before you posted your answer. – Barmar Aug 03 '17 at 20:26
  • @Barmar: I was answering the original and confirming on jsFiddle so I didn't see the edits. Just a note on your answer, sometimes updating the property does alter the attribute. https://jsfiddle.net/41qaL10t/ – spanky Aug 03 '17 at 20:33
  • Hmm, is that only true for boolean properties? Or just specific attributes? Is there a spec you can refer to? – Barmar Aug 03 '17 at 20:36
  • 2
    @Barmar - Bi-directional reflection of properties and attributes is hugely complicated, but goes well beyond boolean properties. The spec is the HTML5 spec, particularly [section 4](http://w3c.github.io/html/semantics.html#semantics). When reading it, the thing to note is that the spec uses slightly different terminology. What you call a "property" the spec calls an "IDL attribute", and what you call an "attribute" the spec calls a "content attribute". You will often see "The ... IDL attribute must reflect the content attribute of the same name." but the sometimes different names are used. – Alohci Aug 04 '17 at 01:12
  • 3
    For example:The label element: "The htmlFor IDL attribute must reflect the for content attribute." or the meta element "The IDL attribute relList must reflect the rel content attribute." In particular, given your answer, for the input element: "The defaultValue IDL attribute must reflect the value content attribute." – Alohci Aug 04 '17 at 01:21
  • 1
    Ahh, that's what I was missing -- the property used by the `Reset` button is the `defaultValue` IDL attribute, not the `value` attribute. – Barmar Aug 04 '17 at 14:10
4

This is to keep the HTML and DOM synchronized, because at some point CSS selectors will be checking the DOM element and relying on the attributes to be accurate.

If the DOM isn't accurate, then the CSS won't be accurate either. What if HTML didn't bother to reflect attributes back to the DOM?

Let's say the text of an input field is initially black, and you want the text to be red when it is disabled. Now let's say the user did something and a function you wrote disabled the input field through javascript.

If HTML didn't reflect that 'disabled' attribute back to the DOM, CSS would NEVER KNOW that the element was disabled.

So the text color would never be changed to red. Remember, CSS checks and relies on DOM attributes. If HTML doesn't change the DOM attributes, for all CSS cares about, nothing has changed so everything will remain the same.

For a less technical analogy, let's say CSS is Batman, HTML is Gotham Police Department, an Attribute is the bat-signal, and the DOM is the sky.

Batman(css) constantly checks the sky(dom) to see if his bat-signal light(attribute) is being shown by the Gotham Police Department(html). If there was some event(an attribute changed) which happened in Gotham where the Gotham Police Department(html) needed Batman(css) to help, but they just didn't bother to send him an update through the sky(dom) with the bat-signal(attribute update), Batman would never know there was a job that needs to be done.

I mean he's an awesome super hero so he would eventually find out but sadly, CSS is no Batman.

  • The disabled attribute is a bit of a strange beast. Even if the disabled attribute wasn't reflected, preventing the [disabled] attribute selector from working, a browser could conceivably implement the :disabled pseudo-class to be based on the DOM property itself. – BoltClock Aug 12 '17 at 16:09
3

The article speaks about custom elements, and takes the example of a <div> element with it's natural behaviour for some properties like hidden or disabled.

So, first of all, don't take the sentence you mention as a directive from your god, because it's not.

Simply, if you have an application with some css using the disasbled property for specific styling, be aware that, if you want to :

  1. create some custom elements
  2. manipulate their attributes through Javascript, including disasbled
  3. see the css applied for disasbled property of custom elements you are manipulating

Then, yes, you'll need to reflect back to DOM

3

Well, this is the first question I'm answering but I'll try either way.

To be honest, it's kinda hard to tell what you're asking but if you're looking to reflect HTMLElement property changes back on the DOM (via attributes). Then here's the code (using HTMLElement's):

// Defines a new property on an Object.
Object.defineProperty(HTMLElement.prototype, "someAttribute", {
    // Configurable
    configurable: true,

    // Enumerable
    enumerable: true,

    /* Getter
        (Allows you get the value like this =>
            element.someAttribute // returns the value of "someAttribute"
        )
    */
    get: function() {
        return this.getAttribute("someAttribute")
    },

    /* Setter
        (Allows you to modify/ update the value like this => 
            element.someAttribute = "lorem ipsum"
        )
    */
    set: function(data) {
        this.setAttribute("someAttribute", data)
    }
})

Hope this answered your question.

Lapys
  • 936
  • 2
  • 11
  • 27