Skip to content

Instantly share code, notes, and snippets.

@schmidsi
Last active March 10, 2021 09:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save schmidsi/2719fef211df9160a43808d505e30b4e to your computer and use it in GitHub Desktop.
Save schmidsi/2719fef211df9160a43808d505e30b4e to your computer and use it in GitHub Desktop.
Scribble to fix scroll position
function MyApp({ Component, pageProps }) {
return <ScrollPositionProvider>
<Component {...pageProps} />
</ScrollPositionProvider>
}
export default MyApp
import React, { useEffect, useContext } from 'react';
import Router from 'next/router';
export const ScrollPositionContext = React.createContext({
triggerScroll: () => null,
});
export const useScrollPosition = () => useContext(ScrollPositionContext);
let routerBound = false;
let shouldScrollRestore = false;
let scrollRestored = false;
const cachedScrollPositions: {
[asPath: string]: { x: number; y: number };
} = {};
/**
* - Save the scroll position mapped to the current url on navigation
* - Mark shouldScrollRestore on beforePopState to trigger scrollTo only for back/next navigation
* - Remove scroll position when normally navigating in: Refresh, links
* - (Re-)trigger scrollTo after all content is loaded with triggerScroll
*
* Inspired by https://github.com/zeit/next.js/issues/3303#issuecomment-562581796
*/
const ScrollPositionProvider = ({ children }) => {
useEffect(() => {
if (typeof window === 'undefined') return;
// Commented out because I'm not sure if its necessary
// if (window?.history?.scrollRestoration) {
// window.history.scrollRestoration = 'manual';
// }
const handleChangeStart = () => {
cachedScrollPositions[Router.asPath] = {
x: window.scrollX || window.pageXOffset,
y: window.scrollY || window.pageYOffset,
};
scrollRestored = false;
};
const handleChangeComplete = () => {
if (shouldScrollRestore && cachedScrollPositions[Router.asPath]) {
const { x, y } = cachedScrollPositions[Router.asPath];
window.scrollTo(x, y);
shouldScrollRestore = false;
scrollRestored = true;
}
};
if (!routerBound) {
routerBound = true;
Router.events.on('routeChangeStart', handleChangeStart);
Router.events.on('routeChangeComplete', handleChangeComplete);
}
Router.beforePopState(() => {
shouldScrollRestore = true;
return true;
});
// // Commented out because not sure if it works
// return () => {
// router.events.off('routeChangeStart', handleChangeStart);
// router.events.on('routeChangeComplete', handleChangeComplete);
// };
});
const triggerScroll = () => {
if (scrollRestored && cachedScrollPositions[Router.asPath]) {
const { x, y } = cachedScrollPositions[Router.asPath];
window.scrollTo(x, y);
}
};
return (
<ScrollPositionContext.Provider value={{ triggerScroll }}>
{children}
</ScrollPositionContext.Provider>
);
};
export default ScrollPositionProvider;
@sahar75
Copy link

sahar75 commented Feb 23, 2021

if I want to scroll a div with specific id instead of window what am I suppose to do?

@schmidsi
Copy link
Author

@sahar75 Probably replace window.scrollTo with document.getElementById('YOUR_DIV_ID').scrollTo

@schmidsi
Copy link
Author

And window.scrollX, etc. accordingly

@sahar75
Copy link

sahar75 commented Mar 3, 2021

@sahar75 Probably replace window.scrollTo with document.getElementById('YOUR_DIV_ID').scrollTo

that doesn't work unfortunately

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment