Skip to main content
March 23, 2026Dan Rodney/8 min read

GreenSock: ScrollTrigger on Multiple Alternating Elements

Master GSAP ScrollTrigger for dynamic web animations

GSAP ScrollTrigger Core Concepts

Individual Element Targeting

Learn to apply ScrollTrigger animations to multiple elements separately, avoiding the common mistake of animating all elements simultaneously.

Dynamic Direction Control

Master alternating animation directions for each row, creating engaging visual patterns that draw elements toward the center.

Performance Optimization

Discover techniques to prevent unwanted horizontal scrolling and maintain smooth animations across different viewport sizes.

Topics Covered in This JavaScript Tutorial:

Creating Individual ScrollTrigger Animations for Multiple Elements, Implementing Alternating Animation Directions for Visual Harmony, Resolving Horizontal Scroll Issues in Complex Animations

Exercise Preview

preview multiple scrolltriggers

Animation Pattern Overview

This tutorial creates alternating scroll-triggered animations where images and text slide toward each other from opposite directions, creating a dynamic storytelling effect as users scroll through content sections.

Exercise Overview

In this comprehensive exercise, you'll master the art of applying ScrollTrigger animations to multiple elements simultaneously while maintaining individual control over each animation. This technique is essential for creating sophisticated, professional scroll experiences that enhance user engagement without overwhelming the interface. You'll learn to avoid common pitfalls that plague even experienced developers and implement best practices that ensure smooth, performant animations across different viewport sizes.

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 to understand the baseline experience.

    • Scroll down and observe the alternating sections that each feature a photo paired with descriptive text.
    • Notice how the static layout feels flat—we'll transform these sections into dynamic, engaging elements that respond beautifully to user scroll behavior.
  4. Keep the page open in your browser for quick testing as we build the animation system.

Initial Setup Process

1

Open Project Files

Navigate to Desktop > Class Files > JavaScript Class > GSAP-ScrollTrigger-Multiple folder and open in your code editor

2

Preview the Layout

Open index.html in browser and scroll to observe the alternating sections with photos and text that need animation

3

Prepare Development Environment

Ensure GSAP core and ScrollTrigger plugins are properly linked and ready for animation implementation

Setting up a ScrollTrigger Animation

We'll begin by creating a foundational animation, then iterate and refine it to achieve professional-grade results. This methodical approach helps you understand each component's role in the final animation.

  1. Since we're already linked to the GSAP core and ScrollTrigger plugins, we can immediately start coding our animation system. At the bottom of index.html, add this initial tween within the script tag to create a smooth fade-in effect with subtle horizontal movement:

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

    • Scroll to the top and reload the page to reset all animations.
    • Scroll down slowly and observe how the first image slides in from the left while fading in—this creates a professional entrance effect.
    • Continue scrolling to the next image with the Convenient heading. Position your viewport so you can see portions of multiple images simultaneously.
    • Reload the page again and you'll discover a critical flaw: all images animate simultaneously rather than individually. This is one of the most frequent ScrollTrigger mistakes developers encounter, extensively documented at greensock.com/st-mistakes.

      The solution requires applying separate ScrollTrigger instances to each image through iteration. Before implementing this fix, let's perfect the animation parameters.

  3. To unlock advanced scrollTrigger configuration options, we need to convert the trigger from a simple string to an object. Change scrollTrigger from 'section img' to {}:

    gsap.from('section img', {scrollTrigger:{}, X:-100, opacity:0});
  4. Improve code readability by adding strategic line breaks—this becomes crucial as animations grow more complex:

    gsap.from('section img', {
       scrollTrigger:{}, 
       X:-100, opacity:0
    });
  5. Restore the trigger selector and enable visual debugging markers to understand the animation timeline:

    gsap.from('section img', {
       scrollTrigger:{trigger:'section img', markers:true}, 
       X:-100, opacity:0
    });
  6. Fine-tune the animation timing and add scrub functionality for smooth, scroll-linked motion:

    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 test the enhanced animation in your browser.

    • Return to the top and reload to reset all animations.
    • Scroll down and notice how the first image now smoothly scrubs with your scroll position, creating a more organic, connected feeling between user input and visual response.
Common ScrollTrigger Mistake

Using a single ScrollTrigger for multiple elements causes all elements to animate simultaneously. This is one of the most frequent errors developers make when starting with ScrollTrigger.

ScrollTrigger Configuration Options

Trigger Points

Set start and end points using percentages like 'top 85%' and 'bottom 80%' to control when animations begin and complete.

Scrub Animation

Link animation progress directly to scroll position using scrub values, creating smooth scroll-controlled motion effects.

Making ScrollTrigger Work with Each Image Individually

Now we'll solve the fundamental issue of multiple elements sharing a single animation instance. This technique is essential for creating scalable animation systems that work regardless of content quantity.

  1. Create a variable to store the complete array of target images, enabling programmatic access to each element:

    <script>
       let sectionImg = document.querySelectorAll('section img');
  2. Implement a for loop to iterate through each image, creating individual ScrollTrigger instances:

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

    Code Omitted To Save Space

    });
    };
  3. Replace the generic selector with specific array references based on the loop counter. This ensures each animation targets only its corresponding element:

    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 to test the individualized animation system.

    • Start from the page top and scroll down methodically, observing how each image now animates independently as it enters the viewport.
    • While the individual triggering works perfectly, the uniform left-to-right motion feels repetitive. Since images alternate sides, we should make them animate toward the center, creating visual harmony and drawing attention to the content relationship.

Individual Element Animation Setup

1

Create Element Array

Use document.querySelectorAll('section img') to create a collection of all target images

2

Implement For Loop

Wrap the ScrollTrigger animation in a for loop to iterate through each image individually

3

Target Individual Elements

Replace string selectors with array elements using sectionImg[i] to ensure each image gets its own trigger

Alternating Directions for Each Row

Professional scroll animations often employ alternating patterns to create visual rhythm and prevent monotony. We'll implement a dynamic system that calculates appropriate movement directions based on element position.

  1. To create alternating directions, we'll dynamically calculate X values using positive and negative amounts. Start by storing the base movement distance as a variable for easy adjustment:

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

    Code Omitted To Save Space

    });
    };

    NOTE: Notice our animation currently uses -100, but this variable-based approach will provide more flexibility for the alternating system we're building.

  2. Replace the hard-coded value with our dynamic 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 conditional logic to detect odd versus even positions and adjust movement direction accordingly:

    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: The modulus operator (%) returns the remainder from division. Even numbers divided by 2 have no remainder (0), while odd numbers have a remainder (1). This mathematical property provides a reliable way to alternate behavior.

    The negative assignment flips the movement direction. Since arrays are zero-indexed, sectionImg[0] gets -100 (matching our original effect), while sectionImg[1] gets +100 (opposite direction).

  4. Save and test the alternating animation system.

    • The images should now animate inward toward their associated text, creating a more sophisticated visual flow.
    • Next, we'll apply the same logic to text elements, ensuring they move in the opposite direction so both text and images converge toward the center, maximizing visual impact.
Remainder Operator Technique

The modulo operator (%) determines if array index i is even or odd. Even numbers (i % 2 == 0) have no remainder, while odd numbers do, enabling alternating animation directions.

Direction Logic Implementation

Even Row Animation

Array index 0, 2, 4 etc. use negative movement values to animate elements from left toward center.

Odd Row Animation

Array index 1, 3, 5 etc. use positive movement values to animate elements from right toward center.

Animating the Text

Coordinated text animation completes the professional effect, with text and images moving toward each other to create focal points that guide user attention naturally.

  1. Create a dedicated variable for text elements, maintaining consistency with our image array approach:

    <script>
       let sectionImg = document.querySelectorAll('section img');
       let sectionText = document.querySelectorAll('section.text');
  2. Duplicate the gsap.from() animation to create a foundation for text animation:

    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. Update the second animation to target text elements instead of images:

    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. Create the convergence effect by making text move in the opposite direction from its paired image:

    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 test the complete coordinated animation system.

    Both text and images should now animate inward toward each other, creating compelling focal points that enhance content hierarchy and user engagement.

Text Animation Integration

1

Create Text Element Array

Add let sectionText = document.querySelectorAll('section.text') to target all text sections

2

Duplicate Animation Code

Copy the image animation gsap.from() method and modify it for text elements

3

Apply Opposite Direction

Use -movement for text animation to create converging motion where text and images move toward each other

Hiding the Markers

With animations perfected, we'll remove the debugging markers for a clean production experience while preserving the ability to re-enable them for future development.

  1. Disable the markers in both animations by changing true to false:

    NOTE: Keeping the marker property with false values allows quick re-enabling during future debugging sessions—a professional development practice.

  2. Save and test the cleaned-up animation experience.

    • The page now presents a polished, production-ready appearance without debugging overlays.
    • Narrow your browser window until images begin scaling down responsively.
    • You may notice unwanted horizontal scrolling capability, even though no content should extend beyond the viewport width.

    This horizontal scroll issue occurs because elements animate from positions outside the viewport, temporarily expanding the page width. This is a common side effect of scroll animations that requires a specific CSS solution.

Development Best Practice

Keep the markers property in your code even when set to false. This allows quick debugging by switching back to true when troubleshooting animation timing issues.

Fixing Unwanted Horizontal Scrolling

Professional scroll animations require careful overflow management to prevent unintended scrolling behaviors that degrade user experience, particularly on mobile devices where accidental horizontal scrolls are especially problematic.

  1. In your code editor, navigate to the css folder and open main.css to implement the overflow solution.
  2. Add a new CSS rule above the existing body rule to prevent horizontal overflow while preserving vertical scrolling:

    html, body {
       overflow-X: hidden;
    }
  3. Save the CSS changes and reload the page for final testing.

    • Horizontal scrolling issues should be completely resolved, providing a smooth, intentional user experience.
    • Congratulations! You've successfully created a professional-grade scroll animation system that combines individual element control, sophisticated directional logic, and proper overflow management—skills essential for modern web development.

CSS Overflow Hidden Solution

Pros
Prevents unwanted horizontal scrolling caused by off-screen element positioning
Maintains responsive design integrity across different viewport sizes
Simple CSS solution that doesn't affect animation performance
Cons
May hide intentionally overflowing content if not applied carefully
Requires testing across different screen sizes to ensure no content is cut off

Key Takeaways

1Use document.querySelectorAll() and for loops to apply individual ScrollTrigger animations to multiple elements, avoiding the common mistake of animating all elements simultaneously
2Implement alternating animation directions using the modulo operator (i % 2 == 0) to detect even/odd array indices and create dynamic visual patterns
3Configure ScrollTrigger with start/end points like 'top 85%' and 'bottom 80%' combined with scrub values to create smooth scroll-controlled animations
4Apply opposite movement directions to related elements (images and text) using positive and negative values to create converging motion effects
5Use CSS overflow-x: hidden on html and body elements to prevent unwanted horizontal scrolling caused by off-screen element positioning
6Enable ScrollTrigger markers during development for debugging, but keep the property available for future troubleshooting even when set to false
7Structure animations with proper trigger targeting using array elements rather than string selectors to ensure each element receives its own ScrollTrigger instance
8Test responsive behavior by resizing the browser window to identify and fix layout issues that may arise from animated element positioning

RELATED ARTICLES