【问题标题】:How do I force Elm to reuse a DOM element?如何强制 Elm 重用 DOM 元素?
【发布时间】:2020-08-14 22:04:00
【问题描述】:

我想使用 Elm 制作一个包含一些视频元素的 Web 应用程序,其动态布局可以根据浏览器的宽度和高度进行更改。当我尝试这样做时,很明显 Elm 正在为视频生成新元素,但这不起作用,因为视频元素具有需要保留的状态。

为了简单起见,我用计数器而不是视频来演示这个问题。我试图用Html.lazyKeyed.node 解决这个问题,但它仍然存在。

这里的代码也可以从https://github.com/ijt/elm-dom-elt-reuse克隆过来。

src/Main.elm:

port module Main exposing (..)

import Browser
import Html exposing (..)
import Html.Attributes as Attribute exposing (id, style)
import Html.Events exposing (onClick)


main =
    Browser.element
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }


type alias Model =
    { layout : Layout }


type Layout
    = Row
    | Column


init : () -> ( Model, Cmd Msg )
init _ =
    ( { layout = Row }, startCounters () )


type Msg
    = ToggleLayout


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ToggleLayout ->
            let
                l2 =
                    case model.layout of
                        Row ->
                            Column

                        Column ->
                            Row
            in
            ( { model | layout = l2 }, Cmd.none )


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick ToggleLayout ] [ text "Toggle Layout" ]
        , counters model
        ]


counters : Model -> Html Msg
counters model =
    case model.layout of
        Column ->
            div []
                [ div [] [ counter1 ]
                , div [] [ counter2 ]
                ]

        Row ->
            div [] [ counter1, spacer, counter2 ]


spacer : Html Msg
spacer =
    text " "


counter1 : Html Msg
counter1 =
    span [ id "counter1" ]
        [ text "0" ]


counter2 : Html Msg
counter2 =
    span [ id "counter2" ]
        [ text "0" ]


port startCounters : () -> Cmd msg

static/index.html:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8">
  <title>Main</title>
  <style>body { padding: 0; margin: 0; }</style>
</head>

<body>

<pre id="elm"></pre>

<script src="Main.js"></script>
<script>
  window.app = Elm.Main.init( { node: document.getElementById("elm") } );

  window.app.ports.startCounters.subscribe(function() {
    let c1 = document.getElementById("counter1");
    let c2 = document.getElementById("counter2");
    function increment(e) {
      let n = parseInt(e.innerText);
      e.innerText = n + 1;
    }
    requestAnimationFrame(function() {
      setInterval(function() {
        increment(c1);
        increment(c2);
      }, 100)
    })
  });
</script>

</body>
</html>

Makefile:

static/Main.js: src/Main.elm
    elm make src/Main.elm --output=static/Main.js

【问题讨论】:

    标签: elm


    【解决方案1】:

    保持元素在 DOM 树中的深度始终相同。

    这是新代码:

    counters : Model -> Html Msg
    counters model =
        let
            d =
                case model.layout of
                    Column ->
                        "column"
    
                    Row ->
                        "row"
        in
        div
            [ style "flex-direction" d
            , style "display" "flex"
            ]
            [ counter1
            , counter2
            ]
    
    
    counter1 : Html Msg
    counter1 =
        span [ id "counter1", style "padding" "8px" ]
            [ text "0" ]
    
    
    counter2 : Html Msg
    counter2 =
        span [ id "counter2", style "padding" "8px" ]
            [ text "0" ]
    
    
    port startCounters : () -> Cmd msg
    

    感谢 Elm Slack 频道上的 jessta 提出这个想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-08
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2018-04-24
      • 2020-12-08
      • 2011-10-24
      • 2012-07-22
      相关资源
      最近更新 更多