Skip to content

Commit 03132c0

Browse files
committed
Merge branch 'pr-259' into stack-1.0.1
fixes elm#259 fixes elm#228 fixes elm#148 fixes elm/virtual-dom#122 fixes elm/virtual-dom#169 fixes elm/virtual-dom#144
2 parents 1affbf3 + cfe4863 commit 03132c0

2 files changed

Lines changed: 99 additions & 67 deletions

File tree

properties-vs-attributes.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,14 @@ Notice that the attribute is called `class` but the property is called `classNam
1212

1313
It is actually a bit crazier than that though. **Sometimes an attribute exists, but there is no corresponding property.** For example, as of this writing the `webkit-playsinline` can be added with `setAttribute`, but there is no corresponding property. And with SVG, you cannot use properties at all, you must to use `setAttributeNS` for everything.
1414

15-
With all the corner cases here, it makes sense to have access to both approaches.
15+
With all the corner cases here, it makes sense to have access to both approaches.
16+
17+
The functions in `Html.Attributes` are generally implemented using attributes (except for a few things that _have_ to be properties). Using attributes have a couple of benefits for standard attributes:
18+
19+
1. It’s possible to remove an attribute, but not always possible to remove a property. For example, the HTML `<a>` results in a link without any `href`. The specification defines that as a link placeholder, and a use case is a menu where the current page does not need to link to itself. But `.href` is actually set to the empty string (`""`) on the DOM node. The HTML `<a href="/about">` results in a link _with_ a `href`. If you have just `<a>`, you can either do `.href = "/about"` or `.setAttribute("href", "/about")` to set the `href`. But how do you remove the attribute again? If you try `.href = ""`, that actually results in `<a href="">` (which is not the same as the `href` attribute being missing), while `.removeAttribute("href")` does result in `<a>`.
20+
21+
2. When initializing an Elm app, you give it a DOM node to render into, or the `<body>` node is used. What happens if the node to render into isn’t empty? Elm then _virtualizes_ the DOM into virtual DOM. If the DOM nodes match what your app’s first render, the virtual DOM diffing won’t find any changes to make. This allows for server side rendering HTML, and then have Elm take over that content and avoiding lots of work at startup. Let’s take `<a href="/about">` as an example again. When virtualizing, Elm has to make a guess: Did you use `property "href" (Json.Encode.string "/about")` or `attribute "href" "/about"` in your Elm code? If we guess wrong, the first render will do an unnecessary DOM update, and if we guess right the first render won’t do anything as expected. Another complication is that attributes and properties don’t always have the same name, as mentioned above. When virtualizing DOM nodes, it’s possible to loop over all _attributes_ that are set, but it’s not possible to loop over properties. For `<div class="user-info">`, a loop over attributes would get that `class` attribute, but if `Html.Attributes.class` is implemented with a property, we would have to translate `class` into `className` with a lookup table. By primarily using attributes in `Html.Attributes`, we don’t need a lookup table (except for a couple of edge cases).
22+
23+
3. Some properties are read only – if you try to assign them an error is thrown. The most notable example of this is trying to do `.className = "my-class"` on an SVG element. That throws an error, while `.setAttribute("class", "my-class")` works. In Elm, both `Html msg` and `Svg msg` are type aliases for the same virtual DOM node type, so you can mix functions from elm/html and elm/svg without getting type errors. `Html.Attributes.class` used to be implemented by setting the `className` property, and a common mistake was accidentally using using `Html.Attributes.class` on an SVG element, instead of `Svg.Attributes.class` (which is implemented by setting the `class` attribute), which would then cause hard to debug runtime errors. By setting the `class` attribute in `Html.Attributes.class` this issue is avoided.
24+
25+
4. Attributes are easier to diff. If we take the example with the link again, if you do `.href = "/about"`, then what is the value of `.href` afterwards? You might think it’s `"/about"`, but it’s actually `"https://example.com/about"` (if the code is running on `https://example.com`). `.href` returns the _full_ URL. If you set it to a non-full URL, the browser resolves it to the full URL. This is important because some properties change automatically through user interactions. For example, `.value` of a text input updates automatically as the user types into it. But if you have code like `Html.input [ Html.Attributes.value "hardcoded" ]` you would expect the input to always say “hardcoded”. If the virtual DOM diffing compares the old and new virtual DOM, it’ll see that both of them say that `value` should be `"hardcoded"` and decided that no update is needed. But in reality, the input displays whatever the user has typed. For this reason, it sounds like a good idea to instead diff the new virtual DOM against the actual DOM node when it comes to properties. The downside is the `href` example, though: If the virtual DOM says that `href` should be `"/about"` it will never be equal to the `.href` property on the DOM node, since it’s `"https://example.com/about"`, which would result in us setting `.href = "/about"` on _every_ render. Again, this is a reason to prefer attributes where possible. `value` is a good example of where a _property_ is used on purpose: We _need_ to set it on every render to make sure that it is set to the desired value.

0 commit comments

Comments
 (0)