In Elm I have a simple image, and I want it to be replaced by some 'missing' image onerror. So I added an "onerror" attribute:
img
[ src "broken-link.png"
, attribute "onerror" "this.onerror=null;this.src='missing.png';"
] []
However, when I look at the generated html, the img doesn't get an onerror
attribute, but rather gets an data-onerror
, and ofcourse this doesn't work.
Why is this? And how do I fix it?
Here is a little example I made with my friend Bulbasaur to illustrate the problem: https://ellie-app.com/3Yn8Y6Rmvrqa1
This seems to be a built-in undocumented safety feature of Elm.
Checking source code of Elm, Html.attribute
is defined as (source)
attribute : String -> String -> Attribute msg
attribute =
VirtualDom.attribute
and VirtualDom.attribute
is defined as (source):
attribute : String -> String -> Attribute msg
attribute key value =
Elm.Kernel.VirtualDom.attribute
(Elm.Kernel.VirtualDom.noOnOrFormAction key)
(Elm.Kernel.VirtualDom.noJavaScriptOrHtmlUri value)
Your attribute name onclick
is passed to Elm.Kernel.VirtualDom.noOnOrFormAction
which is defined in JavaScript as (source):
function _VirtualDom_noOnOrFormAction(key)
{
return /^(on|formAction$)/i.test(key) ? 'data-' + key : key;
}
So if attribute name starts with on
, or is string formAction
, then it is renamed to be a data-attribute.
One way I know how to fix this is to write the code in Elm without JavaScript. Here's full working example with main parts copied below: (This is based on accepted answer here about detecting image load failure.)
1) Keep current URL in model
type alias Model =
{ src : String
}
init : Model
init =
{ src = "http://example.com" }
2) Change event handler to send Elm message on image load error
img
[ src model.src
, on "error" (Json.Decode.succeed ImageError)
, alt "Should be Bulbasaur"
] []
3) Change URL in update
on error
update : Msg -> Model -> Model
update msg model =
case msg of
ImageError ->
{ model
| src = "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png"
}
I'd solve this problem by writing a custom event handler for image load error event. The custom handler will be look a like:
onImageLoadError : msg -> Html.Attribute msg
onImageLoadError message =
on "error" (JD.succeed message)
I'll define the Model
, Msg
as below:
type Msg
= ImageLoadError
type alias Model =
ImageSrc
I'll also define a custom type to wrap the good and default url.
type ImageSrc
= Good String
| Default String
The onImageLoadError
will be fired and send the ImageLoadError
msg to the update
function, and the update
function will then set the default url as:
update : Msg -> Model -> Model
update msg model =
case msg of
ImageLoadError ->
Default "https://developer.mozilla.org/static/img/web-docs-sprite.22a6a085cf14.svg"
And here is the full runnable code:
module Main exposing (main)
import Browser
import Html exposing (Html, div, img, text)
import Html.Attributes exposing (alt, src)
import Html.Events exposing (on)
import Json.Decode as JD
type Msg
= ImageLoadError
type ImageSrc
= Good String
| Default String
type alias Model =
ImageSrc
update : Msg -> Model -> Model
update msg model =
case msg of
ImageLoadError ->
Default "https://developer.mozilla.org/static/img/web-docs-sprite.22a6a085cf14.svg"
imageSrcVal : ImageSrc -> String
imageSrcVal src =
case src of
Good url ->
url
Default url ->
url
view : Model -> Html Msg
view model =
div []
[ div [] [ text "Broken image, that should be replaced by Bulbasaur: " ]
, img
[ src (imageSrcVal model)
, alt "Should be Bulbasaur"
, onImageLoadError ImageLoadError
]
[]
, div [] [ text "instead it does nothing, and the 'onerror' attribute is 'data-onerror' (use inspect element to see)" ]
]
onImageLoadError : msg -> Html.Attribute msg
onImageLoadError message =
on "error" (JD.succeed message)
main : Program () Model Msg
main =
Browser.sandbox
{ init = Good "https://developer.mozilla.org/static/arrows/arrow-right.cbc8b4f075cc.svg"
, view = view
, update = update
}
Ellie App.
I init the model with a good url "https://developer.mozilla.org/static/arrows/arrow-right.cbc8b4f075cc.svg", but if you somehow make it a bad url like "https://bad.developer.mozilla.org/static/arrows/arrow-right.cbc8b4f075cc.svg", you will the default image there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With