This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
i'm trying to scroll to a new item after it's been added. what's a good way to do this after the DOM updates?(see TODO)
#?(:clj (defonce !items (atom [])))
(e/def items (e/server (e/watch !items)))
#?(:clj
(defn add-item! [id]
(swap! !items conj {:item/id [id]})))
(e/client
(div
(ui/button
(e/fn []
(e/server (add-item! (util/uuid)))
;; TODO: wait for item to be added and fetch its dom node
(.scrollIntoView new-dom-node))
(text "Add an item")))
(e/for-by :item/id [{:item/keys [title]} items] (div title)))
When we do stuff like this, we usually stick the scroll logic in the item to be scrolled to itself. This is because race conditions become a non-issue, and because it’s the “correct” place in terms of colocation. Of course, it requires some way of knowing that an item is new though, or it’ll run on every page load.
works! in the button handler, i add the item id to a set tracking new items:
(swap! !new-items conj new-id)
and the item handles the scrolling and removing itself from the new item set
(when (@!new-items id)
(when-let [node (.getElementById js/document id)]
(.scrollIntoView node))
(swap! !new-items disj id))
is there a way to reference the DOM node within its electric function, without resorting to getElementById?If you e/watch
!new-items
instead of dereferencing it, you effectively have a way to scroll to an item at any point in time, not just when initially adding them. Just a note for the future.
Make sure to e/watch
once and pass down the value to the children in that case. Don’t e/watch
once per child.
@U06B8J0AJ would you mind elaborating on this e/watch
!new-items
being able to scroll to an item at any point in time? i thought about it a bit and still don't get it, but it seems interesting. maybe a (pseudo-)code snippet to illustrate?
Sure! I only mean that scrolling doesn’t necessarily have anything to do with adding. For example:
(e/defn Bar
[should-scroll]
(dom/div
(when should-scroll
(.scrollIntoView dom/node))))
(e/defn Foo
[]
(let [!scroll-to (atom nil)
scroll-to (e/watch !scroll-to)]
(e/for-by :id [{:keys [id]} [{:id 0} {:id 1} {:id 2}]]
(Bar. (= id scroll-to)))))
You can set !scroll-to
to an ID and have it scroll for any reason. For example when adding an item, but you could also set it from the URL: my.app/?focus=1
or something, to support direct linking.