Search code examples
elm

Implementing autoscroll with jumpToBottom


The documentation for setViewportOf includes a function jumpToBottom.

Below is a small example program which uses it:

module Main exposing (..)

import Browser
import Html             exposing (..)
import Html.Events      exposing (..)
import Html.Attributes  exposing (..)
import Browser.Dom
import Task

type Msg = AddText | NoOp

type alias Model = { messages : List String }

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

update : Msg -> Model -> (Model, Cmd Msg)
update msg model = 
    case msg of
        AddText -> ({ model | messages = model.messages ++ [ "abc" ]}, jumpToBottom "messages")
        NoOp -> (model, Cmd.none)

view : Model -> Html Msg
view model =
    div [ id "outer" ]
    [
        div [ id "messages" ]
        [
            ul [] (List.map (\message -> li [] [ text message ]) model.messages)
        ],
        button [ onClick AddText ] [ text "Add" ]
    ]

init : () -> (Model, Cmd Msg)
init _ = ({ messages = [] }, Cmd.none)

main = Browser.element { init = init, view = view, update = update, subscriptions = (\_ -> Sub.none) }

This program can be pasted into Try Elm to test it out.

Each time you press the button, text will be added to the page. Eventually, more text than can fit on the screen will be added and the scrollbar will appear. I'd like for the page to scroll automatically, but that doesn't seem to be happening.

If I change

jumpToBottom "messages"

to

jumpToBottom "outer"

that does not seem to have an effect.

Any suggestions are welcome! :-)


Solution

  • This is a CSS layout problem. To better understand, add this to your page CSS (you can test this on https://ellie-app.com/ ).

        #messages { 
          max-height: 200px;
          overflow-y:auto;
        }
    

    The above makes messages a scrollable element that can have its viewport set. Without some kind of constrain on the height, the messages div will just expand as needed and the viewport will always be the same as the scene, or, in other words, it will never scroll.