onClick 消息导致不必要的页面刷新

onClick message causes unwanted refreshing of the page

我正在尝试用 Elm 建立一个网站,它包含几个更改语言的链接:

div [ class "language" ][ a [ class englishClass, onClick (ChangeLanguage English) ] [ text "EN" ]
                        , a [ class germanClass, onClick (ChangeLanguage German) ] [ text "DE" ]
                        , a [ class italianClass, onClick (ChangeLanguage Italian) ] [ text "IT" ]
                        ]

我的更新是这样的:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ...  -- Stuff for URLs

        ChangeLanguage language ->
            ( { model | language = language }
            , Cmd.none
            )

type Msg
    = LinkClicked Browser.UrlRequest
    | UrlChanged Url.Url
    | ChangeLanguage Language

这是我的模型:

type alias Model =
    { key : Nav.Key
    , url : Url.Url
    , language : Language
    }

这是我的初始化函数,它(至少在我看来)默认使用英语作为语言:

init : flags -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init _ url key = ( Model key url English, Cmd.none )

问题:

每当我单击更改语言的链接时,它们 确实 似乎有效:我看到页面中段落的语言发生了变化,但随后发生了一些不希望的事情:

  1. 页面刷新。由于我已经看到整个页面的语言变化,很明显这不是必需的,所以我想避免它。
  2. 作为 1 的结果,视口再次一直带到页面顶部。
  3. 语言再次更改为默认英文!

我怎样才能避免 1(以及 2)的发生?
我缺少什么才能使更改在整个网站上保持不变(至少在刷新页面时,因为我还没有尝试使用会话或 cookie)?

我认为此行为是 Elm 中的一个错误,并已提交 an issue for it, as have others with associated PRs。但是在一年半的时间里,没有人注意到任何能够真正对此做些什么的人,不幸的是,这与 Elm 的课程是一样的。

问题在于 Elm "hijacks" onClicka 元素上创建导航事件,以便可以在 Elm 内部处理锚点。当使用 Browser.application 时,单击 a 元素将使 Elm 调用 onUrlReuqest,值为 Browser.UrlRequest,这将 class 或 URL 作为InternalExternal 并要求您决定如何处理它。

问题的根源在于,省略 href 将生成 External UrlRequest,其中 URL 为空字符串。默认情况下,通过 External 的通常处理,这将告诉浏览器照常加载 URL,从而刷新页面。但它也表明一个可能的解决方法是特殊情况 External "":

update msg model =
    case msg of
        UrlRequested (Browser.Internal _) ->
            ( model, Cmd.none )

        UrlRequested (Browser.External "") ->
            ( model, Cmd.none )

        UrlRequested (Browser.External url) ->
            ( model, Browser.Navigation.load url )

        UrlChanged _ ->
            ( model, Cmd.none )

另一种解决方法是将 href="#" 添加到 a 元素,这将正确地将它们 class 化为 Internal