hotwired / turbo

The speed of a single-page web application without having to write any JavaScript
https://turbo.hotwired.dev
MIT License
6.65k stars 421 forks source link

Page refresh scrolls to top in Firefox (Linux Desktop) #505

Open alexandergitter opened 2 years ago

alexandergitter commented 2 years ago

In Firefox 95 (Linux, tested on Ubuntu 20.04), Turbo causes the browser to jump back to the top of the page on a normal page refresh (F5) .

Here's some example HTML to test this. Usually it works fine for the first refresh, but on subsequent ones the page jumps back to the top.

<!DOCTYPE html>
<html>

<head>
  <script type="module">
    import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';
  </script>
</head>

<body>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
  <h1>Hello, world!</h1>
</body>

</html>
Epskampie commented 2 years ago

Got the same issue. This makes development on Firefox with livereload hugely annoying as you've got to scroll down manually on every change.

I've found a workaround: Turbo.session.pageObserver.stop(); but it may break other stuff depending on whether you're using turbo drive.

kevinlang commented 11 months ago

Also still getting this issue on Firefox.

  1. Go to https://turbo.hotwired.dev/ (which has Turbo)
  2. Scroll down partway
  3. Refresh

It'll scroll to the top every time.

kuba-orlik commented 1 week ago

Can also confirm, this happens for me in Firefox 129 on Linux, and on Firefox for Android 131

4lllex commented 2 days ago

When you refresh a page relinquishControlOfScrollRestoration restores history.scrollRestoration back to auto so the browser would handle scroll on full page refresh:

https://github.com/hotwired/turbo/blob/3163a066c0156565eb5f5541d4ea6f225a3c012f/src/core/drive/history.js#L74-L79

Then when page loads turbo assumes control over the scroll for subsequent visits:

https://github.com/hotwired/turbo/blob/3163a066c0156565eb5f5541d4ea6f225a3c012f/src/core/drive/history.js#L67-L72

Except in Firefox history.scrollRestoration change doesn't seem to persist over the refresh (or probably it is set too late) and remains manual which sets this.previousScrollRestoration to manual as well, which means browser doesn't handle the scroll and turbo can't handle the scroll yet.


Here is a simpler way to reproduce it:

// don't forget to remove Turbo for this test

addEventListener("pagehide", function(event) {
  history.scrollRestoration = "manual"
  console.log("pagehide", history.scrollRestoration)
});

addEventListener("pageshow", function(event) {
  console.log("pageshow", history.scrollRestoration)
});

In Chrome:

// first visit
pageshow auto
// refresh
pagehide manual
Navigated to http://0.0.0.0:3000/
pageshow manual

In Firefox:

// first visit
pageshow auto 
// refresh
Navigated to http://0.0.0.0:3000/
pagehide manual
pageshow auto                         // ???
// refresh
Navigated to http://0.0.0.0:3000/
pagehide manual
pageshow manual

Not sure what the proper way of dealing with this would be, but here is a patch to fix it for now:

Turbo.session.history.__proto__.relinquishControlOfScrollRestoration = function() {
  if (this.previousScrollRestoration) {
    // history.scrollRestoration = this.previousScrollRestoration;
    history.scrollRestoration = "auto";
    delete this.previousScrollRestoration;
  }
}