10

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

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

直观地说,我似乎可以调用port运行 JavaScript 代码的 a element.scrollTop = element.scrollHeight

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

问题是在更新之前scrollToBottom被调用。所以没什么大不了的,我将其转换为. 但是,即使现在首先更新,modelTaskmodelview还没有更新。所以我最终从底部滚动到第二个项目!

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

4

4 回答 4

8

您可以使用端口滚动到列表的底部。
在这种情况下,您需要设置一个 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 ! []

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

于 2016-12-10T17:41:16.093 回答
6

从 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)
于 2020-06-21T17:07:29.650 回答
1

由于不推荐使用 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" ]
于 2019-01-18T23:27:23.707 回答
1

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

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

查看说滚动到底部的文档: toBottom : Id -> Task Error (),您可以看到它需要是一个任务,因此您可以换成Task.attempt. 例如: Task.attempt (\_ -> NoOp) (Dom.Scroll.toBottom Id)

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

于 2018-01-12T17:19:09.530 回答