Jump to content


Using ScrollTrigger to change selected menu item?

Recommended Posts

So `ScrollTrigger.isInViewport()` can tell you if an element is visible, but is there a way to use this to work out the 'most' visible, or most recently visible? I'd basically like to switch which menu item is active based on which element is visible, rather than just when it hits a certain point on screen

I just thought I'd ask if there was a simple way to do this before I started working out a way to track the changes :)

*Edit: This approach almost works, but flicks to 4 (or I'm guessing to whatever the default of the ternary statement is I'm using)

See the Pen RwBOdQP?editors=1111 by nwoodward (@nwoodward) on CodePen

Link to comment
Share on other sites

16 minutes ago, ryan_labar said:

I think your best bet for this would be to create a loop for each section, and set the start and end values for each section, then use toggleClass or onEnter/onEnterBack callbacks to update the classes.



Thanks Ryan. Yeah I did consider onEnter until I saw there was an isVisible function. You're probably right though, I can't think of an easier way to do it, just thought I'd check

Link to comment
Share on other sites

Actually, having reworked my codepen, just specifying the amount of element that needs to be visible should make it work as needed. Going to try now


  const section1 = ScrollTrigger.isInViewport(".section1", .6);
  const section2 = ScrollTrigger.isInViewport(".section2", .6);
  const section3 = ScrollTrigger.isInViewport(".section3", .6);
  const section4 = ScrollTrigger.isInViewport(".section4", .6);

Edit: XD That did not work as planned! My idea was if .6 had to be in view then if each element was full screen then there could only ever be one active element. Don't know why that logic doesn't work, but it doesn't!

Link to comment
Share on other sites



I think Ryan's advice is the best course of action for this:

const itemNumber = document.querySelector('.item-number');
const sections = gsap.utils.toArray(".section");

sections.forEach((section, i) => {
    trigger: section,
    start: "top center",
    onEnter: () => itemNumber.innerText = i + 1,
    onLeaveBack: () => itemNumber.innerText = i,

Here is a live example:

See the Pen XWBQwgp by GreenSock (@GreenSock) on CodePen


Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

I updated the pen with your changes @Rodrigo, an actual menu and a dummy section, in case it's useful to anyone later. I'll probably add a @Carl inspired underline animation too :)

I'm a bit surprised though thatonLeaveBack: () => setActive(sectionsMap.get(sections[i - 1]))isn't erroring though when scrolling back up?

See the Pen NWBmZgd?editors=1011 by nwoodward (@nwoodward) on CodePen

Link to comment
Share on other sites

Just added the smooth scroll effect on clicking individual menu items - it's a shame it sets the intermediate menu items as active, think that looks pretty ugly 😕

*Edit: maybe if I debounce the setActive function so it'll only work on the last call? Edit: Nope :D

Link to comment
Share on other sites

29 minutes ago, Rodrigo said:



Maybe preventOverlaps could help in this scenario:


Happy Tweening!

That is a cool feature, but it doesn't seem to work for me - I'm assuming because it impacts animations rather than their callbacks? ie, it's the `setActive` call that needs to be skipped, rather than the animation itself

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.