diff --git a/src/Html/Styled/Attributes.elm b/src/Html/Styled/Attributes.elm
index fb128d1e..4f636749 100644
--- a/src/Html/Styled/Attributes.elm
+++ b/src/Html/Styled/Attributes.elm
@@ -238,11 +238,21 @@ property =
VirtualDom.Styled.property
-stringProperty : String -> String -> Attribute msg
-stringProperty key string =
- VirtualDom.Styled.property key (Json.string string)
+{-| This is used for attributes that have a property that:
+- Is boolean.
+- Defaults to `false`.
+- Removes the attribute when setting to `false`.
+Note:
+
+- Some properties, like `checked`, can be modified by the user.
+- `.setAttribute(property, "false")` does not set the property to `false` – we have to remove the attribute. (Except `spellcheck` which explicitly has a "false" (and "true") value.)
+
+Consider `hidden : Bool -> Attribute msg`. When that `Bool` is `True`, we could implement the function with `attribute "hidden" ""`. (Using the empty string seems to be “canonical”, but any string would make the element hidden.) But what do we do when the `Bool` is `False`? The intention is to make the element _not_ hidden. The only way of doing that is to remove the `hidden` attribute, but we cannot do that with `attribute` – it always results in the attribute being present (we can only choose its value, but no value will result in the element _not_ being hidden). To keep this API, we _have_ to use the `hidden` _property_ instead, which (like mentioned above) automatically removes the attribute when set to `false`.
+
+An alternative would be to have `hidden : Attribute msg` and let users do `if shouldHide then hidden else ???` where `???` would have to be a way to express a no-op `Attribute msg`, or the user has to resort to list manipulation.
+-}
boolProperty : String -> Bool -> Attribute msg
boolProperty key bool =
VirtualDom.Styled.property key (Json.bool bool)
@@ -285,7 +295,7 @@ you will get both classes!
-}
class : String -> Attribute msg
class =
- stringProperty "className"
+ VirtualDom.Styled.attribute "class"
{-| Indicates the relevance of an element.
@@ -300,14 +310,14 @@ attribute must be unique.
-}
id : String -> Attribute msg
id =
- stringProperty "id"
+ VirtualDom.Styled.attribute "id"
{-| Text to be displayed in a tooltip when hovering over the element.
-}
title : String -> Attribute msg
title =
- stringProperty "title"
+ VirtualDom.Styled.attribute "title"
@@ -318,14 +328,19 @@ title =
-}
accesskey : Char -> Attribute msg
accesskey char =
- stringProperty "accessKey" (String.fromChar char)
+ VirtualDom.Styled.attribute "accessKey" (String.fromChar char)
{-| Indicates whether the element's content is editable.
+
+Note: These days, the contenteditable attribute can take more values than a boolean, like "inherit" and "plaintext-only". You can set those values like this:
+
+ attribute "contenteditable" "inherit"
-}
contenteditable : Bool -> Attribute msg
-contenteditable =
- boolProperty "contentEditable"
+contenteditable bool =
+ -- Note: `node.contentEditable = 'bad'` throws an error!
+ VirtualDom.Styled.attribute "contenteditable" (if bool then "true" else "false")
{-| Defines the ID of a `menu` element which will serve as the element's
@@ -341,7 +356,7 @@ contextmenu =
-}
dir : String -> Attribute msg
dir =
- stringProperty "dir"
+ VirtualDom.Styled.attribute "dir"
{-| Defines whether the element can be dragged.
@@ -355,7 +370,7 @@ draggable =
-}
dropzone : String -> Attribute msg
dropzone =
- stringProperty "dropzone"
+ VirtualDom.Styled.attribute "dropzone"
{-| -}
@@ -368,14 +383,14 @@ itemprop =
-}
lang : String -> Attribute msg
lang =
- stringProperty "lang"
+ VirtualDom.Styled.attribute "lang"
{-| Indicates whether spell checking is allowed for the element.
-}
spellcheck : Bool -> Attribute msg
-spellcheck =
- boolProperty "spellcheck"
+spellcheck bool =
+ VirtualDom.Styled.attribute "spellcheck" (if bool then "true" else "false")
{-| Overrides the browser's default tab order and follows the one specified
@@ -395,7 +410,7 @@ tabindex n =
-}
src : String -> Attribute msg
src url =
- stringProperty "src" url
+ VirtualDom.Styled.attribute "src" url
{-| Declare the height of a `canvas`, `embed`, `iframe`, `img`, `input`,
@@ -419,7 +434,7 @@ width n =
-}
alt : String -> Attribute msg
alt =
- stringProperty "alt"
+ VirtualDom.Styled.attribute "alt"
@@ -453,7 +468,7 @@ loop =
-}
preload : String -> Attribute msg
preload =
- stringProperty "preload"
+ VirtualDom.Styled.attribute "preload"
{-| A URL indicating a poster frame to show until the user plays or seeks the
@@ -461,7 +476,7 @@ preload =
-}
poster : String -> Attribute msg
poster =
- stringProperty "poster"
+ VirtualDom.Styled.attribute "poster"
{-| Indicates that the `track` should be enabled unless the user's preferences
@@ -476,7 +491,7 @@ default =
-}
kind : String -> Attribute msg
kind =
- stringProperty "kind"
+ VirtualDom.Styled.attribute "kind"
@@ -484,7 +499,7 @@ kind =
{-| Specifies a user-readable title of the text `track`. -}
label : String -> Attribute msg
label =
- stringProperty "label"
+ VirtualDom.Styled.attribute "label"
--}
@@ -492,7 +507,7 @@ label =
-}
srclang : String -> Attribute msg
srclang =
- stringProperty "srclang"
+ VirtualDom.Styled.attribute "srclang"
@@ -504,7 +519,7 @@ srclang =
-}
sandbox : String -> Attribute msg
sandbox =
- stringProperty "sandbox"
+ VirtualDom.Styled.attribute "sandbox"
{-| An HTML document that will be displayed as the body of an `iframe`. It will
@@ -512,7 +527,7 @@ override the content of the `src` attribute if it has been specified.
-}
srcdoc : String -> Attribute msg
srcdoc =
- stringProperty "srcdoc"
+ VirtualDom.Styled.attribute "srcdoc"
@@ -524,15 +539,19 @@ srcdoc =
-}
type_ : String -> Attribute msg
type_ =
- stringProperty "type"
+ VirtualDom.Styled.attribute "type"
{-| Defines a default value which will be displayed in a `button`, `option`,
`input`, `li`, `meter`, `progress`, or `param`.
-}
value : String -> Attribute msg
-value =
- stringProperty "value"
+value string =
+ -- Note: `.value` has no corresponding attribute. It can also be modified by
+ -- the user by typing in inputs.
+ -- Properties are diffed against the actual DOM, not the virtual DOM, so
+ -- this ensures that the DOM is up-to-date with the model.
+ VirtualDom.Styled.property "value" (Json.string string)
{-| Indicates whether an `input` of type checkbox is checked.
@@ -547,7 +566,7 @@ checked =
-}
placeholder : String -> Attribute msg
placeholder =
- stringProperty "placeholder"
+ VirtualDom.Styled.attribute "placeholder"
{-| Defines which `option` will be selected on page load.
@@ -566,21 +585,21 @@ For `form` and `input`.
-}
accept : String -> Attribute msg
accept =
- stringProperty "accept"
+ VirtualDom.Styled.attribute "accept"
{-| List of supported charsets in a `form`.
-}
acceptCharset : String -> Attribute msg
acceptCharset =
- stringProperty "acceptCharset"
+ VirtualDom.Styled.attribute "accept-charset"
{-| The URI of a program that processes the information submitted via a `form`.
-}
action : String -> Attribute msg
action uri =
- stringProperty "action" uri
+ VirtualDom.Styled.attribute "action" uri
{-| Indicates whether a `form` or an `input` can have their values automatically
@@ -588,7 +607,7 @@ completed by the browser.
-}
autocomplete : Bool -> Attribute msg
autocomplete bool =
- stringProperty "autocomplete"
+ VirtualDom.Styled.attribute "autocomplete"
(if bool then
"on"
@@ -619,7 +638,7 @@ text/plain.
-}
enctype : String -> Attribute msg
enctype =
- stringProperty "enctype"
+ VirtualDom.Styled.attribute "enctype"
{-| Associates an `input` with a `datalist` tag. The datalist gives some
@@ -653,7 +672,7 @@ maxlength n =
-}
method : String -> Attribute msg
method =
- stringProperty "method"
+ VirtualDom.Styled.attribute "method"
{-| Indicates whether multiple values can be entered in an `input` of type
@@ -670,7 +689,7 @@ in form submits. For `button`, `form`, `fieldset`, `iframe`, `input`,
-}
name : String -> Attribute msg
name =
- stringProperty "name"
+ VirtualDom.Styled.attribute "name"
{-| This attribute indicates that a `form` shouldn't be validated when
@@ -686,7 +705,7 @@ against.
-}
pattern : String -> Attribute msg
pattern =
- stringProperty "pattern"
+ VirtualDom.Styled.attribute "pattern"
{-| Indicates whether an `input` or `textarea` can be edited.
@@ -719,7 +738,7 @@ for an `output`.
-}
for : String -> Attribute msg
for =
- stringProperty "htmlFor"
+ VirtualDom.Styled.attribute "for"
{-| Indicates the element ID of the `form` that owns this particular `button`,
@@ -740,7 +759,7 @@ date, the max value must be a number or date. For `input`, `meter`, and `progres
-}
max : String -> Attribute msg
max =
- stringProperty "max"
+ VirtualDom.Styled.attribute "max"
{-| Indicates the minimum value allowed. When using an input of type number or
@@ -748,7 +767,7 @@ date, the min value must be a number or date. For `input` and `meter`.
-}
min : String -> Attribute msg
min =
- stringProperty "min"
+ VirtualDom.Styled.attribute "min"
{-| Add a step size to an `input`. Use `step "any"` to allow any floating-point
@@ -756,7 +775,7 @@ number to be used in the input.
-}
step : String -> Attribute msg
step n =
- stringProperty "step" n
+ VirtualDom.Styled.attribute "step" n
@@ -782,7 +801,7 @@ values are "hard" and "soft".
-}
wrap : String -> Attribute msg
wrap =
- stringProperty "wrap"
+ VirtualDom.Styled.attribute "wrap"
@@ -804,7 +823,7 @@ E.g. `"#planet-map"`.
-}
usemap : String -> Attribute msg
usemap =
- stringProperty "useMap"
+ VirtualDom.Styled.attribute "usemap"
{-| Declare the shape of the clickable area in an `a` or `area`. Valid values
@@ -813,7 +832,7 @@ include: default, rect, circle, poly. This attribute can be paired with
-}
shape : String -> Attribute msg
shape =
- stringProperty "shape"
+ VirtualDom.Styled.attribute "shape"
{-| A set of values specifying the coordinates of the hot-spot region in an
@@ -821,7 +840,7 @@ shape =
-}
coords : String -> Attribute msg
coords =
- stringProperty "coords"
+ VirtualDom.Styled.attribute "coords"
@@ -834,7 +853,7 @@ coords =
-}
align : String -> Attribute msg
align =
- stringProperty "align"
+ VirtualDom.Styled.attribute "align"
{-| Contains a URI which points to the source of the quote or change in a
@@ -842,7 +861,7 @@ align =
-}
cite : String -> Attribute msg
cite =
- stringProperty "cite"
+ VirtualDom.Styled.attribute "cite"
@@ -853,7 +872,7 @@ cite =
-}
href : String -> Attribute msg
href url =
- stringProperty "href" url
+ VirtualDom.Styled.attribute "href" url
{-| Specify where the results of clicking an `a`, `area`, `base`, or `form`
@@ -869,7 +888,7 @@ You can also give the name of any `frame` you have created.
-}
target : String -> Attribute msg
target =
- stringProperty "target"
+ VirtualDom.Styled.attribute "target"
{-| Indicates that clicking an `a` and `area` will download the resource
@@ -887,7 +906,7 @@ The empty `String` says to just name it whatever it was called on the server.
-}
download : String -> Attribute msg
download fileName =
- stringProperty "download" fileName
+ VirtualDom.Styled.attribute "download" fileName
{-| Indicates that clicking an `a` and `area` will download the resource
@@ -896,14 +915,14 @@ So `downloadAs "hats.json"` means the person gets a file named `hats.json`.
-}
downloadAs : String -> Attribute msg
downloadAs =
- stringProperty "download"
+ VirtualDom.Styled.attribute "download"
{-| Two-letter language code of the linked resource of an `a`, `area`, or `link`.
-}
hreflang : String -> Attribute msg
hreflang =
- stringProperty "hreflang"
+ VirtualDom.Styled.attribute "hreflang"
{-| Specifies a hint of the target media of a `a`, `area`, `link`, `source`,
@@ -919,7 +938,7 @@ media =
-}
ping : String -> Attribute msg
ping =
- stringProperty "ping"
+ VirtualDom.Styled.attribute "ping"
{-| Specifies the relationship of the target object to the link object.
@@ -967,7 +986,7 @@ besides 1.
-}
start : Int -> Attribute msg
start n =
- stringProperty "start" (String.fromInt n)
+ VirtualDom.Styled.attribute "start" (String.fromInt n)
@@ -987,7 +1006,7 @@ headers for this cell. For `td` and `th`.
-}
headers : String -> Attribute msg
headers =
- stringProperty "headers"
+ VirtualDom.Styled.attribute "headers"
{-| Defines the number of rows a table cell should span over.
@@ -1003,7 +1022,7 @@ colgroup, rowgroup.
-}
scope : String -> Attribute msg
scope =
- stringProperty "scope"
+ VirtualDom.Styled.attribute "scope"
{-| Specifies the URL of the cache manifest for an `html` tag.
@@ -1018,5 +1037,5 @@ manifest =
{-| The number of columns a `col` or `colgroup` should span. -}
span : Int -> Attribute msg
span n =
- stringProperty "span" (String.fromInt n)
+ VirtualDom.Styled.attribute "span" (String.fromInt n)
--}
diff --git a/src/VirtualDom/Styled.elm b/src/VirtualDom/Styled.elm
index 37297ad4..553df456 100644
--- a/src/VirtualDom/Styled.elm
+++ b/src/VirtualDom/Styled.elm
@@ -260,9 +260,9 @@ type Scope
= Scope String
-encodeScope : Scope -> Json.Encode.Value
+encodeScope : Scope -> String
encodeScope (Scope scope) =
- Json.Encode.string scope
+ scope
{-| Like map, but allows specifying an initial list to build on top of.
@@ -548,9 +548,9 @@ unstyleScopedNS maybeNonce scope ns elemType properties children =
styleNode =
toStyleNode maybeNonce (ScopedStyles scope rootStyles descendantStyles)
- -- Ensure that our embedded id is the last property on the root node. This should be less confusing if the user accidentally specifies their own id.
+ -- Ensure that our embedded id is the last attribute on the root node. This should be less confusing if the user accidentally specifies their own id.
unstyledProperties =
- mapOnto (extractUnstyledAttributeNS rootStyles) properties [ VirtualDom.property "id" (encodeScope scope) ]
+ mapOnto (extractUnstyledAttributeNS rootStyles) properties [ VirtualDom.attribute "id" (encodeScope scope) ]
in
VirtualDom.nodeNS ns
elemType
@@ -606,9 +606,9 @@ unstyleScoped maybeNonce scope elemType properties children =
styleNode =
toStyleNode maybeNonce (ScopedStyles scope rootStyles descendantStyles)
- -- Ensure that our embedded id is the last property on the root node. This should be less confusing if the user accidentally specifies their own id.
+ -- Ensure that our embedded id is the last attribute on the root node. This should be less confusing if the user accidentally specifies their own id.
unstyledProperties =
- mapOnto (extractUnstyledAttribute rootStyles) properties [ VirtualDom.property "id" (encodeScope scope) ]
+ mapOnto (extractUnstyledAttribute rootStyles) properties [ VirtualDom.attribute "id" (encodeScope scope) ]
in
VirtualDom.node
elemType
@@ -667,9 +667,9 @@ unstyleScopedKeyedNS maybeNonce scope ns elemType properties keyedChildren =
keyedStyleNode =
toKeyedStyleNode maybeNonce (ScopedStyles scope rootStyles descendantStyles) keyedChildNodes
- -- Ensure that our embedded id is the last property on the root node. This should be less confusing if the user accidentally specifies their own id.
+ -- Ensure that our embedded id is the last attribute on the root node. This should be less confusing if the user accidentally specifies their own id.
unstyledProperties =
- mapOnto (extractUnstyledAttributeNS rootStyles) properties [ VirtualDom.property "id" (encodeScope scope) ]
+ mapOnto (extractUnstyledAttributeNS rootStyles) properties [ VirtualDom.attribute "id" (encodeScope scope) ]
in
VirtualDom.keyedNodeNS
ns
@@ -726,9 +726,9 @@ unstyleScopedKeyed maybeNonce scope elemType properties keyedChildren =
keyedStyleNode =
toKeyedStyleNode maybeNonce (ScopedStyles scope rootStyles descendantStyles) keyedChildNodes
- -- Ensure that our embedded id is the last property on the root node. This should be less confusing if the user accidentally specifies their own id.
+ -- Ensure that our embedded id is the last attribute on the root node. This should be less confusing if the user accidentally specifies their own id.
unstyledProperties =
- mapOnto (extractUnstyledAttribute rootStyles) properties [ VirtualDom.property "id" (encodeScope scope) ]
+ mapOnto (extractUnstyledAttribute rootStyles) properties [ VirtualDom.attribute "id" (encodeScope scope) ]
in
VirtualDom.keyedNode
elemType
@@ -814,10 +814,10 @@ extractUnstyledAttribute styles (Attribute val isCssStyles cssTemplate) =
if isCssStyles then
case Dict.get cssTemplate styles of
Just classname ->
- VirtualDom.property "className" (Json.Encode.string classname)
+ VirtualDom.attribute "class" classname
Nothing ->
- VirtualDom.property "className" (Json.Encode.string "_unstyled")
+ VirtualDom.attribute "class" "_unstyled"
else
val