I add <div>
s to a wrapper <div>
and I need to be able to scroll to the last one added each time. How do I do this in Elm?
<div class="messages" style="height: 7em; overflow: scroll">
<div>Anonymous: Hello</div>
<div>John: Hi</div>
</div>
Intuitively, it seems like I could call a port
that runs the JavaScript code element.scrollTop = element.scrollHeight
:
AddChatMessage chatMessage ->
( { model | chatMessages = model.chatMessages ++ [ chatMessage ] } , scrollToBottomPort "div.messages" )
The problem is scrollToBottom
gets called before the model
gets updated. So no big deal, I convert that into a Task
. But still, even though the model
gets updated first now, the view
does not get updated yet. So I end up scrolling to the 2nd item from the bottom!
This maybe leads to a more general question I'm curious about in Elm, how do you run a Cmd
after the view is updated due to a change in the model?
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With