breadadams / scroller-motion

🛹 Elegant motion scrolling for React
https://scroller-motion.js.org
MIT License
67 stars 5 forks source link

Scroll to anchors not working #3

Closed rijk closed 2 years ago

rijk commented 4 years ago

When clicking a link to an #id further down the page, nothing happens. When scroller-motion is disabled, the page jumps to the anchor, but when it is active, no scroll whatsoever. Probably because there is no React state change?

breadadams commented 4 years ago

@Rijk excuse the delay, I took a look last night and it seems the root cause of the issue is that the anchor's DOM position can't be read (because it's within the scroller-motion element - a position: fixed element).

I'll add this to the roadmap and address it asap, meanwhile you could use a custom solution of locating the offset of the anchored element and then using something like window.scrollTo.

Window scrolling events do work (as you can see in the demo, via the top-right "Scroll down" button).

rijk commented 4 years ago

Ahh, I wouldn't have thought of that 😄 Meanwhile, if you have some time could you help me out with #2 as well? I'd love to contribute to the package.

rijk commented 4 years ago

I used this workaround for now:

const onClick = ( e ) => {
  e.preventDefault()
  const anchor = e.target.getAttribute( 'href' ).substr( 1 )
  window.scrollTo( 0, document.getElementById( anchor ).offsetTop )
}
gabrielperales commented 3 years ago

We can add something in the library to intercept hash changes. We can add a flag or something add this event listener to the component:

useEffect(() => {
  const listenHashChange = (e: HashChangeEvent) => {
    const {newURL} = e
    const [_, id] = newURL.match(/#(.*)$/)

    if (!id) {
      // scroll to top
      window.scrollTo(0, 0)
    }

    const el = document.getElementById(id)
    if (el) {
      // scroll to element
      window.scrollTo(0, el.offsetTop)
    }
  }
  window.addEventListener('hashchange', listenHashChange)

  return () => window.removeEventListener('hashchange', listenHashChange)
}, [])
breadadams commented 3 years ago

I'm thinking that rather than including this in the "core" we either:

  1. Include it in a new Recipes section of the readme (got a few things in mind we could include there)
  2. Export a hook (i.e. useHashScroll) that users can choose to import/apply to their app, for tree-shaking benefits
sosnoski commented 1 year ago

Today I had a similar problem using Next 13 and that solution didn't work. However, it worked using router.events. Hope this can help someone:

  const router = useRouter()
  useEffect(() => {
    const onHashChangeStart = (url: string) => {
      const el = document.getElementById(url.slice(2))
      if (el) {
        window.scrollTo(0, el.offsetTop)
      }
    }
    router.events.on('hashChangeStart', onHashChangeStart)
    return () => {
      router.events.off('hashChangeStart', onHashChangeStart)
    }
  }, [router.events])