Как автоматически прокрутить до конца 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 - это, безусловно, путь, и его легко реализовать.
- Установите вмещающий HTML-элемент, который вы хотите прокрутить, иметь отдельный идентификатор.
- Убедитесь, что у указанного элемента есть стилизация overflow-y: scroll или overflow-x: scroll.
- Отправьте команду из любого сообщения, которое фактически заставляет этот элемент отображаться / открываться (сообщение открывает / отображает элемент, пока команда, которую вы запускаете, выполнит прокрутку). Обратите внимание, что вы используете команду, потому что изменение DOM технически является побочным эффектом.
Глядя на документацию, скажем, прокручиваем до дна:toBottom : Id -> Task Error ()
вы можете видеть, что это должна быть задача, поэтому вы можете заключить в Task.attempt
, Например:
Task.attempt (\_ -> NoOp) (Dom.Scroll.toBottom Id)
Затем вам понадобится функция для преобразования Result Error () в Msg, но поскольку прокрутка вряд ли даст сбой или имеет небольшие недостатки, если это произойдет, вы можете установить фиктивную функцию, например (\_ -> NoOp)
вместо быстрого взлома.