Как автоматически прокрутить до конца div в Elm

Я добавить <div>с оберткой <div> и мне нужно иметь возможность прокручивать последний добавленный каждый раз. Как мне сделать это в Вязов?

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

Интуитивно кажется, что я могу назвать port который запускает код JavaScript element.scrollTop = element.scrollHeight:

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

Проблема в scrollToBottom вызывается до model обновляется. Так что ничего страшного, я превращаю это в Task, Но все же, хотя model обновляется первым сейчас, view пока не обновляется. В итоге я прокручиваю до 2 пункта снизу!

Это может привести к более общему вопросу, который мне интересен в Elm, как вы управляете Cmd после обновления вид из-за смены модели?

2 ответа

Решение

Вы можете использовать порт для прокрутки к нижней части списка.
В этом случае вам нужно установить javascript для ожидания 1 AnimationFrame перед выполнением прокрутки. Чтобы убедиться, что ваш список отображается.

Более простой способ сделать это - использовать библиотеку Elm's Dom.Scroll.
И использовать toBottom функция.

Если вы включите это в свой код следующим образом:

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

    NoOp ->
        model ! []

Он должен работать. Я сделал рабочий пример здесь в runelm.io

Начиная с Elm 0.19, с функциями Browser.Dom.getViewportOf а также Browser.Dom.setViewportOf вы можете переходить в нижнюю часть контейнера каждый раз, когда в него добавляется новый элемент.

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

Начиная с версии 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.Scroll - это, безусловно, путь, и его легко реализовать.

  1. Установите вмещающий HTML-элемент, который вы хотите прокрутить, иметь отдельный идентификатор.
  2. Убедитесь, что у указанного элемента есть стилизация overflow-y: scroll или overflow-x: scroll.
  3. Отправьте команду из любого сообщения, которое фактически заставляет этот элемент отображаться / открываться (сообщение открывает / отображает элемент, пока команда, которую вы запускаете, выполнит прокрутку). Обратите внимание, что вы используете команду, потому что изменение DOM технически является побочным эффектом.

Глядя на документацию, скажем, прокручиваем до дна:toBottom : Id -> Task Error ()вы можете видеть, что это должна быть задача, поэтому вы можете заключить в Task.attempt, Например: Task.attempt (\_ -> NoOp) (Dom.Scroll.toBottom Id)

Затем вам понадобится функция для преобразования Result Error () в Msg, но поскольку прокрутка вряд ли даст сбой или имеет небольшие недостатки, если это произойдет, вы можете установить фиктивную функцию, например (\_ -> NoOp) вместо быстрого взлома.

Другие вопросы по тегам