Search code examples
elm

Elm: attribute "onerror" adds "data-onerror" attribute instead


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


Solution

  • Why is this?

    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.

    How do I fix it?

    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"
                }