Jump to content
Search Community

React + ScrollSmoother, clicking on a button scrolls page to top

merlin test
Moderator Tag

Recommended Posts

Hi all,

 

We encountered an issue with ScrollSmoother where when we change our browser to mobile size (375px) using DevTools in Chrome, every <button type="button"> we click scrolls back to top.

 

I tried to create a CodeSandbox, but since ScrollSmoother is a paid plugin I kinda failed to do that.

 

This is our wrapper (`Box` is a `<div>` and `Flex` is a `<div>` with `display: flex` :

export function BaseLayout({ children, settings }: BaseLayoutProps) {
  useLayoutEffect(() => {
    const smoothScroll = ScrollSmoother.create({
      smooth: 1, // how long (in seconds) it takes to "catch up" to the native scroll position
      smoothTouch: 0.1, // much shorter smoothing time on touch devices (default is NO smoothing on touch devices)
      onUpdate: x => {
        useScrollValue.setState({ scrollValue: x.scrollTop() });
      },
    });

    if (window.location.hash) {
      smoothScroll.scrollTo(window.location.hash, true);
    }

    return () => {
      smoothScroll.kill();
    };
  });

  return (
    <Flex flexDirection="column">
      <Header settings={settings} />

      <Box id="smooth-wrapper" as="main" flex="1 0 auto" display="block">
        <Box
          id="smooth-content"
          background="radial-gradient(74.69% 74.69% at 50% 74.69%, rgba(0, 103, 171, 0.2) 0%, rgba(36, 54, 66, 0.2) 100%), #243642"
          sx={{
            contain: 'layout',
          }}>
          {children}
          <Footer settings={settings} />
        </Box>
      </Box>
    </Flex>
  );
}

 

Link to comment
Share on other sites

I found that one!
We don't unfortunately. We use Next.js.

We're also using the 3.10.4 version of ScrollSmoother.

 

I've just tried putting it outside of the function scope but this doesn't work:

 

ScrollSmoother.create({
  smooth: 1, // how long (in seconds) it takes to "catch up" to the native scroll position
  smoothTouch: 0.1, // much shorter smoothing time on touch devices (default is NO smoothing on touch devices)
  onUpdate: x => {
    useScrollValue.setState({ scrollValue: x.scrollTop() });
  },
});

export function BaseLayout({ children, settings }: BaseLayoutProps) {
  useLayoutEffect(() => {
    const smoothScroll = ScrollSmoother.get();

    if (window.location.hash) {
      smoothScroll.scrollTo(window.location.hash, true);
    }

    return () => {
      smoothScroll.kill();
    };
  }, []);

  return (
    <Flex flexDirection="column">
      <Header settings={settings} />

      <Box id="smooth-wrapper" as="main" flex="1 0 auto" display="block">
        <Box
          id="smooth-content"
          background="radial-gradient(74.69% 74.69% at 50% 74.69%, rgba(0, 103, 171, 0.2) 0%, rgba(36, 54, 66, 0.2) 100%), #243642"
          sx={{
            contain: 'layout',
          }}>
          {children}
          <Footer settings={settings} />
        </Box>
      </Box>
    </Flex>
  );
}

 

Link to comment
Share on other sites

To clarify, your problem is:

  1. You load the page
  2. click a button and it scrolls correctly
  3. you resize the page
  4. click any button and it scrolls to the top of the page

Am I understanding correctly?

A few things:

  1. You should be fine creating the smoother inside of your ULE and that's where I would do it
  2. I wouldn't use setState onUpdate since you can consume the scroll progress anywhere transiently by getting the scrollSmoother

In terms of debugging:

  1. Instead of navigating by hash, calculate the top position of the hash element and pass that and see if the issue remains
  2. Remove as="main" from the wrapper element to see if that changes anything
  3. Replace <Box> with <div> and see if that helps

Outside of that I'm not seeing anything wrong with your setup

  • Like 1
Link to comment
Share on other sites

Hi @SteveS !

Thanks for checking it out.

 

To clarifyL

  1. You load the page
  2. click a button and it scrolls correctly doesn't scroll as expected (it's just a regular <button> no events)
  3. you resize the page to mobile
  4. click any button and it scrolls to the top of the page

I tried adding it outside of function scope, which sometimes broke.

The setState is by `zustand` which is de-coupled from React state so that doesn't affect React's Event Loop

 

So the buttons should never actually scroll the page, we use anchors with hash links for that

EDIT: Changing the `<Box>` elements to regular `<div>` doesn't fix it either

Link to comment
Share on other sites

Yeah I really can't seem to get it to break. Sometimes when I click the button it jumps to center it, but that only happens once and is probably a native DOM thing.

If I had to guess I would say it has something to do with the way you are handling your anchor links or whatever your buttons are supposed to be doing. Your window hash check in the ULE isn't really doing anything. I guess in a full page it would smooth scroll to a position if you navigate to a link with a hash at the end.

Here is the logic I used for smooth scrolling on the page between linked sections. You can extract it into a custom <SectionAnchor /> component or a custom hook. ScrollSmoother.get() returns the current scrollSmoother.

 

  const [currentHash, setCurrentHash] = useState(null);
  useEffect(() => {
    if (!currentHash || currentHash.length < 2) return;
    ScrollSmoother.get().scrollTo(currentHash, true);
    setCurrentHash(null);
  }, [currentHash]);

  const handleClick = (e) => {
    // goes on local anchor onClick
    const sectionHref = e.target.getAttribute("href");
    setCurrentHash(sectionHref);
  };


Oh, also try disabling whatever functionality your buttons have and see if the issue persists.

Link to comment
Share on other sites

@merlin

I'm unable to replicate your issue on my end. My experience with react and GSAP is that you have to keep everything extremely tightly coupled. Disabling components is a start. Based on your description of the issue it is overwhelmingly likely that this is an issue where some logic or functionality is crossing wires. If your buttons don't do anything when clicked, then there is no real way they could be causing a change in the scroll position. It could be that the first click interaction causes a re-render or something.

If you do figure out what happened don't forget to update the thread. I'm interested to see what your issue could be.

Link to comment
Share on other sites

Hiya!

@SteveS @Cassie

We've sort of found the issue.

It seems that removing `smoothTouch` fixed the issue.

We're not sure why or how it became an issue. We'll do some further investigating to see why/how that happened.

In the meantime, if anyone from GSAP official team wants a copy of the repository, just ask! We're working on a client project so can't just open that up to anyone :)

 

Best,

Level30Wizards

  • Like 1
Link to comment
Share on other sites

Thanks for sharing, @merlin. So you're saying that if you completely remove any smoothing from mobile devices (smoothTouch: false), things are fine? Do you have a minimal demo showing the issue (when smoothTouch is enabled)? It'd be SUPER helpful if you isolated it in a minimal demo rather than sending your entire project to weed through, but feel free to send what you can in a DM if you prefer. 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...