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