It doesn’t look “perfect” - sometimes you see the page scroll to top a split-second before the patch destination loads. But it’s been “good enough” for my purposes so far.
I’m not sure of the general feeling about this, but for these types of hook where nothing but the hook itself is going to care about the id, I make a component that encapsulates the hook and generates a random id.
def link_top(assigns) do
~H"""
<.link
patch={~p"/pages/#{@page + 1}"}
phx-hook="ScrollToTop"
id={Ecto.UUID.generate()}
>
Next
</.link>
"""
end
You can use System.unique_integer (combined with some string) or whatever instead of a UUID. I dunno, I haven’t been bitten by this before and can’t think of any reason it would be bad, but maybe there is? You can always make it possible to set and id manually if you need it.
It should be noted I haven’t done this recently and never used it in this exact scenario.
Hey @sodapopcan I believe this may cause unintended behavior. Ecto.UUID.generate() is going to generate a new value each time it is called, which will happen each time link_top is re-rendered, but this is is very difficult to determine. Rendering can be elided based on when changes are detected or not.
Basically, the best practice is to avoid non deterministic rendering.
I suspected this after posting (sorta rubber ducking my thought process). While aware it could change on re-render, it’s never bitten me before, so I thought I’d throw it out there as I’ve never seen it discussed before. Thanks for the explicit answer!
#anchors make sense to me – out of curiosity, what makes it not completely perfect?
Hmm, a more efficient alternative could be adding a single event listener to window that handles the "phoenix.link.click" event triggered by phoenix_html.js as described in the LiveView docs for <.link>:
phoenix_html.js does trigger a custom event phoenix.link.click on the clicked DOM element when a click happened. This allows you to intercept the event on its way bubbling up to window and do your own custom logic…
A data attribute could then be used to distinguish the link elements that should trigger the scroll to top behavior.
<.link patch={~p"/somewhere"} data-scroll="top">Patch to somewhere</.link>
window.addEventListener("phoenix.link.click", function (event) {
var scroll = event.target.getAttribute("data-scroll")
if (scroll == "top") { window.scrollTo(0, 0) }
}, false);