diff options
-rw-r--r-- | pages/hints/attributes-vs-properties.md | 59 | ||||
-rw-r--r-- | src/lustre/attribute.gleam | 6 |
2 files changed, 63 insertions, 2 deletions
diff --git a/pages/hints/attributes-vs-properties.md b/pages/hints/attributes-vs-properties.md new file mode 100644 index 0000000..3614a2a --- /dev/null +++ b/pages/hints/attributes-vs-properties.md @@ -0,0 +1,59 @@ +# Attributes vs Properties + +In the `lustre/attribute` module, there are two functions for constructing a +Lustre `Attribute` value: `attribute` and `property`. There is a subtle but +significant difference between the two! + +## Attributes + +Attributes exist on the HTML element itself. If you open up your browser's dev +tools and inspect the HTML of a page you'll see any attributes you've created. +Likewise if you convert a Lustre `Element` to a string, any attributes will be +serialised into the HTML. + +Attributes are *always* strings, even "boolean" attributes like `disabled` (for +these attributes the existence of the attribute is what makes it true, not the +value). + +In JavaScript, you would set an attribute on an element like so: + +```js +element.setAttribute("value", "wibble") +element.setAttribute("class", "wobble") +``` + +## Properties + +Properties, on the other hand, exist on the DOM object that represents a particular +HTML element. Because they are object properties, they can be of *any* type not +just strings. For example, the `checked` property of an `input` element is a +boolean value, not a string. + +Conversely, that means properties are not serialised into the HTML when you +convert a Lustre `Element` to a string, and they are not reflected in the HTML +when you inspect the page in your browser's dev tools. + +In JavaScript, you would set a property on an element like so: + +```js +element.value = "wibble" +element.className = "wobble" +``` + +Notice how the property names are not always the same as the attribute names, even +when their values are reflected between the two! + +## When to use which + +It's important to know about the element you are trying to render and whether it +is expecting an attribute or a property for a particular value. Often DOM properties +*reflect* attributes in a way that means you can use either, but sometimes that +is not the case. + +For SVG elements, you often cannot use properties at all (they are typically +read-only) and must *always* use attributes. + +Conversely, if you're using Lustre's components and wrote your `on_attribute_change` +decoders in a way that did not anticipate receiving strings you may find that +only properties work as you intended (the [decipher](https://hexdocs.pm/decipher/decipher.html) +package can help you decode number-like strings and booleans from attributes.) diff --git a/src/lustre/attribute.gleam b/src/lustre/attribute.gleam index d8f1cc3..15f322d 100644 --- a/src/lustre/attribute.gleam +++ b/src/lustre/attribute.gleam @@ -21,7 +21,8 @@ pub type Attribute(msg) = /// in JavaScript. Attributes will be rendered when calling [`element.to_string`](./element.html#to_string). /// /// **Note**: there is a subtle difference between attributes and properties. You -/// can read more about the implications of this [here](https://javascript.info/dom-attributes-and-properties). +/// can read more about the implications of this +/// [here](https://github.com/lustre-labs/lustre/blob/main/pages/hints/attributes-vs-properties.md). /// pub fn attribute(name: String, value: String) -> Attribute(msg) { Attribute(name, dynamic.from(value), as_property: False) @@ -32,7 +33,8 @@ pub fn attribute(name: String, value: String) -> Attribute(msg) { /// [`element.to_string`](./element.html#to_string). /// /// **Note**: there is a subtle difference between attributes and properties. You -/// can read more about the implications of this [here](https://javascript.info/dom-attributes-and-properties). +/// can read more about the implications of this +/// [here](https://github.com/lustre-labs/lustre/blob/main/pages/hints/attributes-vs-properties.md). /// pub fn property(name: String, value: any) -> Attribute(msg) { Attribute(name, dynamic.from(value), as_property: True) |