DEV Community

Konnor Rogers
Konnor Rogers

Posted on

13 1

Maintain scroll position in Turbo without data-turbo-permanent

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://dev.to/mikerogers0/persist-scroll-positions-with-hotwire-turbo-1ihk

Why not data-turbo-permanent? Well, in our case we had a sidebar with a highlighted link for the current page, which means link clicks allowed for updating the highlighted current link. There were some workarounds we could have done, but decided not to.

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)
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs