How to auto-scroll to the bottom of a div in Elm

As of Elm 0.19, with functions Browser.Dom.getViewportOf and Browser.Dom.setViewportOf you can go to the bottom of the container everytime the new element is added to it.

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

Since version 0.19 Dom is deprecated instead you can set scroll position using Html.Attribute.property function.

Example

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" ]

You could use a port to scroll to the bottom of a list.
In that case, you need to set a javascript to wait for 1 AnimationFrame before doing the scroll. To make sure your list is rendered.

An easier way to do this is to use Elm's Dom.Scroll library.
And use the toBottom function.

If you include that in your code like this:

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

    NoOp ->
        model ! []

It should work. I've made a working example here in runelm.io


Dom.Scroll is definitely the way to go and is easy to implement.

  1. Set the enclosing HTML element you want to scroll to have a distinct ID.
  2. Make sure said element has styling of overflow-y: scroll or overflow-x: scroll.
  3. Send a command from whatever message actually causes that element to be displayed/opened (the message opens/displays the element while the command you fire will do the scroll). Note you use a command because altering the DOM is technically a side-effect.

Looking at the documentation for say scrolling to the bottom: toBottom : Id -> Task Error (), you can see that it needs to be a Task, so you can wrap into Task.attempt. For example: Task.attempt (\_ -> NoOp) (Dom.Scroll.toBottom Id)

You will then need a function to convert a Result Error () into a Msg, but since scrolling is either unlikely to fail or has little downside if it does, you can set up a dummy function like (\_ -> NoOp) as a quick hack instead.

Tags:

Elm

Elm Port