GreenSock: ScrollTrigger on Multiple Alternating Elements

Free JavaScript Tutorial

Dive into this comprehensive JavaScript tutorial that will guide you through the process of creating ScrollTrigger animations for multiple elements, fixing unwanted horizontal scrolling, and more.

This exercise is excerpted from Noble Desktop’s JavaScript for Front-End training materials and is compatible with JavaScript updates through 2023. To learn current skills in JavaScript with hands-on training, check out our Front-End Web Development Certificate, Full-Stack Web Development Certificate, and coding classes in-person and live online.

Topics covered in this JavaScript tutorial:

Making ScrollTrigger Work with Each Element Individually, Alternating Directions For Each Row, Fixing Unwanted Horizontal Scrolling

Exercise Preview

preview multiple scrolltriggers

Exercise Overview

In this exercise, you’ll create apply ScrollTrigger animations to multiple elements.

JavaScript Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

Getting Started

  1. For this exercise we’ll be working with the GSAP-ScrollTrigger-Multiple folder located in Desktop > Class Files > JavaScript Class. Open that folder in your code editor if it allows you to (like Visual Studio Code does).
  2. In your code editor, open index.html from the GSAP-ScrollTrigger-Multiple folder.
  3. Preview index.html in a browser.

    • Scroll down and notice the alternating sections that each have a photo with text.
    • We’d like these sections to animate in as we scroll down to them.
  4. Leave the page open in the browser, so you can reload it later.

Setting Up a ScrollTrigger Animation

  1. We’re already linked this page to the GSAP core and ScrollTrigger plugins, so we’re ready to start coding our GSAP animation. At the bottom of index.html, in the script tag add a tween that fades in the image as it moves in a bit from the left:

    <script>
       gsap.from('section img', {scrollTrigger:'section img', x:-100, opacity:0});
    </script>
    
  2. Save and switch to the browser.

    • Scroll up to the top and reload the page.
    • Scroll down and see the first image slides in the from the left as it fades in.
    • Scroll down to the next image with the heading Convenient. If possible, make it so you can see some of this photo and the one above (or below it).
    • Reload the page to see that all the images are actually animating at the same time. That’s not what we want. This is one of the most common ScrollTrigger mistakes, which you can read about at greensock.com/st-mistakes

      The solution is to apply a separate ScrollTrigger to each image, which we can do by looping over them. But first let’s perfect this animation.

  3. To set additional scrollTrigger options, we need to change it into an object by changing the scrollTrigger from 'section img' to {} as shown below:

    gsap.from('section img', {scrollTrigger:{}, x:-100, opacity:0});
    
  4. To make things easier to read, add some lines breaks:

    gsap.from('section img', {
       scrollTrigger:{}, 
       x:-100, opacity:0
    });
    
  5. Put the 'section img' trigger back in, and add the markers so we can see what’s going on:

    gsap.from('section img', {
       scrollTrigger:{trigger:'section img', markers:true}, 
       x:-100, opacity:0
    });
    
  6. Change when the animation starts and stops, and link it to the scrollbar so we can scrub through the animation:

    gsap.from('section img', {
       scrollTrigger:{trigger:'section img', start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
       x:-100, opacity:0
    });
    
  7. Save and switch to the browser.

    • Scroll up to the top and reload the page.
    • Scroll down and notice the first image scrolls on (its animation scrubs with the scrollbar), and finishes just a little after it’s fully visible on screen.

Making ScrollTrigger Work with Each Image Individually

Our scroll animation works, but it’s changing all images at the same time. Let’s use a loop to add a ScrollTrigger to each image.

  1. Create a variable for the array (list) of all the images.

    <script>
       let sectionImg = document.querySelectorAll('section img');
    
  2. Wrap a for loop around the scrollTrigger animation:

    let sectionImg = document.querySelectorAll('section img');
    
    for(let i=0; i < sectionImg.length; i++) {
       gsap.from('section img', {
    

    Code Omitted To Save Space

       });
    };
    
  3. Change the element we’re targeting to the image in our array, based on the loop’s counter (i). Be sure to not to leave quotes around the variable/array name:

    for(let i=0; i < sectionImg.length; i++) {
       gsap.from(sectionImg[i], {
          scrollTrigger:{trigger:sectionImg[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:-100, opacity:0
       });
    };
    
  4. Save and reload the page in the browser.

    • Starting at the top of the page, scroll down and notice that each image animates on when it comes onto the screen.
    • It’s great that our animation is now working with each image, but we don’t like how they always animate left to right. Because the images alternate, we’d prefer them always move in toward the center (later we’ll do the same thing with the text so the text and photo will come together).

Alternating Directions For Each Row

  1. To alternate the direction, we’ll have to alternate our x value between positive and negative amounts. Store the current x amount (100) as a variable:

    for(let i=0; i < sectionImg.length; i++) {
       let movement = 100; // odd rows
       gsap.from(sectionImg[i], {
    

    Code Omitted To Save Space

       });
    };
    

    NOTE: You may notice our x value in the ScrollTrigger animation is currently negative 100, but just wait a moment to see how this is going to work.

  2. Swap the hard-coded value (-100) for our new movement variable:

    for(let i=0; i < sectionImg.length; i++) {
       let movement = 100; // odd rows
       gsap.from(sectionImg[i], {
          scrollTrigger:{trigger:sectionImg[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:movement, opacity:0
       });
    };
    
  3. Add a test to detect whether the loop’s counter (i) is odd or even:

    for(let i=0; i < sectionImg.length; i++) {
       let movement = 100; // odd rows
       if( i % 2 == 0 ) {
          movement = -movement; // even rows
       };
       gsap.from(sectionImg[i], {
    

    NOTE: % is a “remainder operator” which returns the remainder (what’s left over) when one number is divided by another number. Even numbers do not have a remainder (so the remainder would be 0, like we’re testing for above), while odd numbers do have a remainder (so the remainder would not be 0).

    Setting a variable to a negative of itself with either change a positive value into a negative, or change a negative value into a positive.

    Remember that arrays start counting with 0, which is an even number (1 is the next number and it’s odd). So sectionImg[0] will be -100 (just like we originally started with) and then sectionImg[1] will be 100.

  4. Save and reload the page in the browser.

    • The row animations should now alternate, with the images always moving inward toward the text.
    • Now let’s apply the same animation to the text, keeping in mind it will need to animate in the opposite direction, so the text and photo will both be moving inward toward each other.

Animating the Text

  1. Create a variable for the array of all the text:

    <script>
       let sectionImg = document.querySelectorAll('section img');
       let sectionText = document.querySelectorAll('section .text');
    
  2. Copy and paste the gsap.from() animation so you end up with two of them:

    for(let i=0; i < sectionImg.length; i++) {
    

    Code Omitted To Save Space

       gsap.from(sectionImg[i], {
          scrollTrigger:{trigger:sectionImg[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:movement, opacity:0
       });
       gsap.from(sectionImg[i], {
          scrollTrigger:{trigger:sectionImg[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:movement, opacity:0
       });
    };
    
  3. In the second gsap.from() change sectionImg to sectionText

       gsap.from(sectionImg[i], {
          scrollTrigger:{trigger:sectionImg[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:movement, opacity:0
       });
       gsap.from(sectionText[i], {
          scrollTrigger:{trigger:sectionText[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:movement, opacity:0
       });
    };
    
  4. In the second gsap.from() add a minus (-) in front of movement to make it the opposite direction as the image (if one is positive the other will be negative, or vice versa):

       gsap.from(sectionImg[i], {
    

    Code Omitted To Save Space

       });
       gsap.from(sectionText[i], {
          scrollTrigger:{trigger:sectionText[i], start:'top 85%', end:'bottom 80%', scrub:1, markers:true}, 
          x:-movement, opacity:0
       });
    };
    
  5. Save and reload the page in the browser.

    Now the text and images should both animate inward towards each other, which looks great!

Hiding the Markers

  1. We no longer need to see the markers so in both places change true to false

    NOTE: We often like to keep the marker property there in case we want to show them again in the future)

  2. Save and reload the page in the browser.

    • The page is looking good now without the markers.
    • Reduce the width of the browser window until the photos start scaling down.
    • You should now be able to scroll horizontally a little bit, even though we didn’t intentionally make anything wider than the page.

    Our page didn’t scroll horizontally before we added the scrolling animation, so why is this happening? It’s because the elements are starting from farther outside and animating into the page, which is causing the page to get wider. We’ll have to hide the “overflow” that gets created to prevent this unwanted scrolling.

Fixing Unwanted Horizontal Scrolling

  1. In your code editor, from the css folder open main.css
  2. Near the top, above the body rule, add the following new rule:

    html, body {
       overflow-x: hidden;
    }
    
  3. Save and reload the page in the browser.

    • Notice the horizontal scrolling problem is fixed.
    • Congrats, you can now enjoying the finished animation scrolling animation!
photo of Dan Rodney

Dan Rodney

Dan Rodney has been a designer and web developer for over 20 years. He creates coursework for Noble Desktop and teaches classes. In his spare time Dan also writes scripts for InDesign (Make Book JacketProper Fraction Pro, and more). Dan teaches just about anything web, video, or print related: HTML, CSS, JavaScript, Figma, Adobe XD, After Effects, Premiere Pro, Photoshop, Illustrator, InDesign, and more.

More articles by Dan Rodney

How to Learn JavaScript

Master JavaScript with hands-on training. JavaScript is one of the world's most widely-used coding languages. Learn JavaScript and its libraries to start creating interactive websites and mobile apps.

Yelp Facebook LinkedIn YouTube Twitter Instagram