Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not reliably restore scroll position using browser/mouse nav buttons in Firefox. #1698

Closed
benjivm opened this issue Oct 5, 2023 · 15 comments

Comments

@benjivm
Copy link

benjivm commented Oct 5, 2023

Version:

  • @inertiajs/vue3 version: ^1.0.11

Describe the problem:

Browser/mouse button page navigation (forward/back) does not restore scroll position. Oddly, it does appear to work fine if the content is static (not served from a controller, e.g.)

Steps to reproduce:

Reproduction repo: https://github.com/benjivm/inertiajs-firefox-scroll-position

Open the page in Firefox (this issue does not affect Chrome).

Scroll down to about the middle of the page where the link and explanation are then navigate with your browser's nav buttons or mouse button.[

This polyfill makes it more reliable but is jarring to use.

This relates to this issue #1459

@Hasan-Mir
Copy link
Contributor

I have the same issue (mostly in Firefox but I think I saw it a few times in Chrome as well).

@ivalkenburg
Copy link

ivalkenburg commented Nov 12, 2023

Having the same issue. It does not always scroll to top when navigating. Specially when further down the page. Very odd.
Maybe its more likely firefox bug rather then inertia?

@fritz-c
Copy link

fritz-c commented Mar 14, 2024

Inertia's popstate listener (popstate being the event called when the back button is used) will asynchronously load the previous component, rendering it when it has loaded.

protected handlePopstateEvent(event: PopStateEvent): void {
if (event.state !== null) {
const page = event.state
const visitId = this.createVisitId()
Promise.resolve(this.resolveComponent(page.component)).then((component) => {
if (visitId === this.visitId) {
this.page = page
this.swapComponent({ component, page, preserveState: false }).then(() => {
this.restoreScrollPositions()
fireNavigateEvent(page)
})
}
})

The problem arises in that the browser wants to restore the previous scroll position as soon as the popstate listener exits, when the url changes, and at that point in time, the previous component has not loaded yet. So what you might be seeing is your pre-back page getting scrolled down (by browser native restoration) but hitting the bottom of the scroll bar, after which point the previous component gets rendered, just at the scroll position you managed to get to from the previous page.

I looked for an elegant solution around this issue, but failing to find one, made one that at least looks good from an end-user's perspective. It's for Vue+Typescript, but could be adapted for other environments. You would want to run this only once, probably in a global layout or something.

import { onMounted, onUnmounted } from 'vue';
import { debounce } from 'lodash-es';
import { router } from '@inertiajs/vue3';

/**
 * Restores the window scroll position when navigating back to a page.
 *
 * The 'popstate' event handler in inertia loads the component from the previous
 * page asynchronously, so browser-native scroll restoration is triggered before
 * the new component is mounted, resulting in the scrollbar banging against the
 * bottom of the window in the case that the page before navigating back is
 * smaller in height than the scroll position to be restored. This composable
 * separately tracks the window scroll position, saves it in the history state
 * and restores it immediately after navigation, avoiding a flash of the
 * incompletely restored scroll position.
 */
export function useWindowScrollRestoration() {
    const saveWindowScroll = debounce((event: Event) => {
        if (event.target === document)
            router.remember(window.scrollY, 'windowScrollY');
    }, 100);
    function restoreWindowScroll() {
        const windowScrollY = router.restore('windowScrollY');
        if (typeof windowScrollY === 'number') {
            window.scrollTo(0, windowScrollY);
        }
    }

    let removeListener = () => {};
    onMounted(() => {
        // Disable browser-native scroll restoration
        window.history.scrollRestoration = 'manual';

        window.addEventListener('scroll', saveWindowScroll, true);
        removeListener = router.on('navigate', restoreWindowScroll);
    });
    onUnmounted(() => {
        window.history.scrollRestoration = 'auto';
        window.removeEventListener('scroll', saveWindowScroll, true);
        removeListener();
    });
}

@pedroborges
Copy link
Collaborator

@benjivm @Hasan-Mir @ivalkenburg @fritz-c please test if #1980 fixes this issue for you.

@benjivm
Copy link
Author

benjivm commented Sep 20, 2024

@pedroborges I installed using npm install inertiajs/inertia#master, will this get me the correct changes for testing?

I still see the issue in the repro repo: https://github.com/benjivm/inertiajs-firefox-scroll-position

@pedroborges
Copy link
Collaborator

@benjivm it's not trivial because Inertia.js uses a monorepo, here's how I did it on one of my projects:

npm i -D https://gitpkg.now.sh/inertiajs/inertia/packages/core?master
cd node_modules/@inertiajs/core
npm i
npm run build
rm -rf node_modules

npm i -D https://gitpkg.now.sh/inertiajs/inertia/packages/svelte?master
cd node_modules/@inertiajs/svelte
npm i
npm run build
rm -rf node_modules

v1.3 will be tagged next week 🤞

@benjivm
Copy link
Author

benjivm commented Sep 20, 2024

I'm using Vue and so changed the 2nd group of commands to the Vue3 package, and unfortunately still see the issue.

@Rubrasum
Copy link

@benjivm I am also using vue3. After trying for a while I found I probably needed to go the long way around, through the contribution guide and clone it, then setup a playground for my app. I took a noob-shot at setting up through a basic package.json change could not get it.

I also have this problem and will check on the release that is fixed. Here is summary of my problem:

I could not get anchor links to work AND consistent scroll position on back/forward navigation. The anchor link problem was fixed by removing smooth scrolling.

EXAMPLE:
If I click an anchor link to another page on my basic lead generation site:

<Link :href="file.href" class="w-full relative"> ... Link to other page and section ... </Link>

I am automatically scrolled to the element (so long as I do not use smooth scrolling). If I use smooth scrolling it scrolls to the wrong location (ok, fine, not a big deal) . But if I press back in either case, the correct page loads at the incorrect scroll position. The position is usually lower than expected.

Here's an example of what I am talking about: https://youtu.be/rfQxhHv5Szs

I will check back at the new release to see if this fixes it. I'm nearing the end of a project so I'd rather not share the repo right now, hope that's ok.

Here's my current versions though:

npm (relevant dependencies and devdependencies together)

        "@inertiajs/inertia": "^0.11.1",
        "@inertiajs/progress": "^0.2.7",
        "@inertiajs/server": "^0.1.0",
        "@inertiajs/vue3": "^1.2.0",
        "laravel-vite-plugin": "^1.0",
        "vite": "^5.0",
        "ziggy-js": "^2.3.0",
        "@vitejs/plugin-vue": "^5.0.0",

composer versions

        "laravel/sail": "^1.29",
        "php": "^8.2",
        "inertiajs/inertia-laravel": "^1.0",
        "laravel/framework": "^11.0",
        "laravel/jetstream": "^5.1",
        "laravel/sanctum": "^4.0",
        "laravel/tinker": "^2.9",
        "tightenco/ziggy": "^2.3"

@rodrigopedra
Copy link

rodrigopedra commented Sep 24, 2024

@Rubrasum how did you remove smooth scrolling?

Never mind. I got it, you disable on Firefox's preferences, right?

I can confirm that disabling smooth scrolling seems to fix the scroll restore.

Although this is not a fix per se, as this options is toggled by default on new Firefox installs, maybe it can guide to better understand how to work around it, or to report a bug to Firefox.

@Rubrasum
Copy link

Rubrasum commented Sep 24, 2024

@Rubrasum how did you remove smooth scrolling?

Although this is not a fix per se, as this options is toggled by default on new Firefox installs, maybe it can guide to better understand how to work around it, or to report a bug to Firefox.

It was part of my own css. I'm using Laravel, Tailwind, and Inertia Vue3. So I have an app.css file here resources/monumental_theme/css/app.css with these line at the top:

@tailwind base;
@tailwind components;
@tailwind utilities;

html {
    scroll-behavior: smooth;
}

So do a search for "scroll-behavior: smooth" Or Tailwind's "scroll-smooth" class.

@rodrigopedra
Copy link

In my case, I didn't have any string "smooth" on my generated CSS. Then I remembered this Firefox's setting:

Screenshot_20240924_190700

Toggling it off did the trick for me.

@Rubrasum
Copy link

Lol, great another bug you found for me too then.

Instead of removing the smooth line, I should've added auto. If you specifically add auto it will override the default firefox setting. I just tested it very haphazardly and seemed to work.

  • Went to localhost on Firefox
  • Tested the link, and it smooth scrolled badly.
  • Updated with...
html {
    scroll-behavior: auto;
}
  • and it worked (jumped straight to the element, effective but not smooth).

@rodrigopedra
Copy link

I'm out of office right now, tomorrow I'll give it a try and let you know.

thanks!

@rodrigopedra
Copy link

Hey @Rubrasum, that seems to fix it! Thanks a lot!

html {
    scroll-behavior: auto;
}

@benjivm
Copy link
Author

benjivm commented Oct 13, 2024

Inertia v2.0.0-beta.1 appears to resolve this issue for me without resorting to the scroll-behavior CSS mentioned above.

@benjivm benjivm closed this as completed Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants