【问题标题】:How to auto-scroll to the bottom of a div in Elm如何在 Elm 中自动滚动到 div 的底部
【发布时间】:2016-12-09 03:19:30
【问题描述】:

我将<div>s 添加到包装器<div> 中,并且我需要能够滚动到每次添加的最后一个。如何在 Elm 中执行此操作?

<div class="messages" style="height: 7em; overflow: scroll">
  <div>Anonymous: Hello</div>
  <div>John: Hi</div>
</div>

直觉上,我似乎可以调用运行 JavaScript 代码 element.scrollTop = element.scrollHeightport

AddChatMessage chatMessage ->
  ( { model | chatMessages = model.chatMessages ++ [ chatMessage ] } , scrollToBottomPort "div.messages" )

问题是scrollToBottom 被调用之前 model 被更新。所以没什么大不了的,我把它转换成Task。但是,即使model 现在首先更新,view 还没有更新。所以我最终滚动到底部的第二个项目!

这可能会导致我在 Elm 中很好奇的一个更普遍的问题,在视图因模型更改而更新后如何运行 Cmd

【问题讨论】:

  • 你可以看看官方的 TodoMVC elm 应用。我认为它就是这样做的。 (现在在移动设备上,否则会有共享链接)。
  • @wintvelt,这是 TodoMVC Elm 应用程序:github.com/evancz/elm-todomvc/blob/master/Todo.elm。我没有看到任何关于滚动的内容,只有一个Dom.focus 电话。 index.html 和 CSS 文件也没有提供任何自动滚动的指示。
  • 你是对的。它不在 todomvc 应用程序中。我的错。将在下面添加答案。

标签: elm elm-port


【解决方案1】:

您可以使用端口滚动到列表底部。
在这种情况下,您需要设置一个 javascript 在滚动之前等待 1 AnimationFrame。确保您的列表已呈现。

更简单的方法是使用 Elm 的 Dom.Scroll 库。
并使用toBottom 函数。

如果您将其包含在您的代码中,如下所示:

case msg of
    Add ->
        ( model ++ [ newItem <| List.length model ]
        , Task.attempt (always NoOp) <| Scroll.toBottom "idOfContainer"
        )

    NoOp ->
        model ! []

它应该工作。 我做了一个工作示例here in runelm.io

【讨论】:

  • 不知道Dom.Scroll!谢谢。我之前曾尝试使用 JavaScript port 调用 Task.perform,但 JavaScript 没有看到最后一项。我想知道这是否是因为Dom.Scroll 在虚拟 dom 上工作,所以它看到了最后一项并且可以向下滚动到它。虽然 JavaScript 还看不到真正的 dom 正在更新......
【解决方案2】:

从 Elm 0.19 开始,使用 Browser.Dom.getViewportOfBrowser.Dom.setViewportOf 函数,您可以在每次向容器中添加新元素时转到容器底部。

jumpToBottom : String -> Cmd Msg
jumpToBottom id =
    Dom.getViewportOf id
        |> Task.andThen (\info -> Dom.setViewportOf id 0 info.scene.height)
        |> Task.attempt (\_ -> NoOp)

【讨论】:

    【解决方案3】:

    Dom.Scroll 绝对是要走的路,而且很容易实现。

    1. 将要滚动的封闭 HTML 元素设置为具有不同的 ID。
    2. 确保所述元素的样式为 overflow-y: scroll 或 溢出-x:滚动。
    3. 从实际的任何消息发送命令 导致该元素被显示/打开(消息 打开/显示元素,而您触发的命令将执行 滚动)。请注意,您使用命令是因为更改 DOM 在技术上是一种副作用。

    查看文档说滚动到底部: toBottom : Id -&gt; Task Error (),你可以看到它需要是一个Task,所以你可以换成Task.attempt。例如: Task.attempt (\_ -> NoOp) (Dom.Scroll.toBottom Id)

    然后您将需要一个函数来将 Result Error () 转换为 Msg,但由于滚动不太可能失败或如果失败的话几乎没有缺点,您可以快速设置一个像 (\_ -&gt; NoOp) 这样的虚拟函数而是破解。

    【讨论】:

    • 写成always NoOp,而不是(\_ -&gt; NoOp)always 是这种通用模式的包装器 :-)。
    【解决方案4】:

    由于版本 0.19 Dom 已弃用,您可以使用 Html.Attribute.property 函数设置滚动位置。

    示例

    import Html exposing (Html, text)
    import Html.Attributes exposing (class, property)
    import Json.Encode as Encode
    
    
    view : Model -> Html Msg
    view model =
        div [ class "flow-editor"
            , property "scrollLeft" (Encode.float model.scrollLeft)
            , property "scrollTop" (Encode.float model.scrollTop)
            ]
            [ text "placeholder" ]
    

    【讨论】:

    • Dom deprecated 来自its README。而且我认为您的答案不适用。问题是关于从更新功能滚动。
    • 我同意你的观点,但我不喜欢在模型逻辑中操作 DOM。对我来说,在更新中进行计算是完全可以的,但是将计算值设置为元素,这应该是视图的关注点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 2010-09-21
    相关资源
    最近更新 更多