skip to Main Content

I am trying to animate four headings as the user scrolls. First I create a sticky positioned div, then the user scrolls over the headings toggling the class .active one at a time, leaving the last one visible and continuing scrolling to the last div.

The problem I am having is that once the .sticky div becomes sticky the browser no longer detects further scrolling until the div becomes non-sticky again.

— UPDATE

I have changed the code to transition sequentially and works better, but I would like to start the animations only when the .sticky div becomes sticky, but I tried that and the scroll animation stops working completely. Also, I would like to keep all headings on the same block but If I do position: absolute on them it breaks the animations too.

const headings = Array.from(document.querySelectorAll('.animated-text'));
const sticky = document.querySelector('.sticky');

let currentActive = null;

window.addEventListener('scroll', () => {
  const viewportHeight = window.innerHeight;
  headings.forEach((heading, index) => {
    const headingRect = heading.getBoundingClientRect();

    if (headingRect.top <= viewportHeight / 2) {
      if (currentActive) {
        currentActive.classList.remove('active');
      }
      heading.classList.add('active');
      currentActive = heading;
    }
  });
});
body {
  margin: 0
}

section {
  position: relative;
}

.sticky {
  padding-bottom: 150px;
  background: #2d232c;
  position: sticky;
  top: 0;
  overflow: hidden;
  height: auto;
  color: white;
}

.animated-text {
  opacity: 0;
  height: 0;
  overflow: hidden;
  transition: opacity 1s ease, height 1s ease, transform 1s ease;
  transform: translateY(0);
}

.animated-text.active {
  height: auto;
  opacity: 1;
  transform: translateY(-20px);
}

.hero, .end {
  height: 100px;
  background: white;
}
<section class='hero'>
  <p>Start</p>
</section>

<section class='sticky'>
  <div class='text-animations'>
    <h1 class='animated-text active'>Intro</h1>
    <h2 class='animated-text'>First</h2>
    <h2 class='animated-text'>Second</h2>
    <h2 class='animated-text'>Third</h2>
  </div>
  <p class='intro_content'>
     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
  </p>  
</section>

<section class='end'>
  <p>End<p>
</section>

2

Answers


  1. It looks like you are trying to create an animation effect for your headings as the user scrolls and the sticky div comes into view. However, the current code only handles the scroll event once, and the scrolling doesn’t continue after the .sticky div becomes sticky.

    To achieve the desired effect of toggling the "active" class for the headings one by one as the user scrolls, you’ll need to continuously check the scroll position and update the active heading accordingly. You can achieve this by using the getBoundingClientRect() method to determine the positions of the headings relative to the viewport.

    const headings = document.querySelectorAll('.animated-text');
    const sticky = document.querySelector('.sticky');
    
    window.addEventListener('scroll', () => {
      const stickyRect = sticky.getBoundingClientRect();
    
      headings.forEach((heading, index) => {
        const headingRect = heading.getBoundingClientRect();
    
        // Check if the heading is inside the sticky div and has come into view
        if (headingRect.top >= stickyRect.top && headingRect.bottom <= stickyRect.bottom) {
          // Add 'active' class to the current heading and remove it from the others
          heading.classList.add('active');
          for (let i = 0; i < headings.length; i++) {
            if (i !== index) {
              headings[i].classList.remove('active');
            }
          }
        }
      });
    });
    body {
      margin: 0;
    }
    section {
      position: relative;
    }
    .sticky {
      padding-bottom: 150px;
      background: #2d232c;
      position: sticky;
      top: 0;
      overflow: hidden;
      height: auto;
      color: white;
    }
    .animated-text {
      opacity: 0;
      max-height: 0;
      transition: opacity 1s ease, max-height 1s ease;
    }
    .animated-text.active {
      max-height: 1000px;
      opacity: 1;   
    }
    .hero, .end {
      height: 100px;
      background: white;
    }
    <!DOCTYPE html>
    <html>
    <head>
      <title>Scroll Animation</title>
     
    </head>
    <body>
      <section class='hero'>
        <p>Hero space</p>
      </section>
    
      <section class='sticky'>
        <div class='text-animations'>
          <h1 class='animated-text active'>Intro</h1>
          <h2 class='h1 animated-text'>First</h2>
          <h2 class='h1 animated-text'>Second</h2>
          <h2 class='h1 animated-text'>Third</h2>
          <h3 class='intro_content'>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
          </h3>
        </div>      
      </section>
    
      <section class='end'>
        <p>Ends<p>
      </section>

    With this updated code, as the user scrolls and the .sticky div becomes sticky, the browser will continue to detect further scrolling and update the "active" class for the headings accordingly, providing the desired animation effect for the headings one by one.

    Login or Signup to reply.
  2. As per the requirementm for positioning all headings at the top, adding

    position:absolute; 
    top:0;
    

    in .active class resolves the problem.

    And for starting animation only when the div becomes sticky, the stickyDiv position needs to be compared and then the animation logic should be implemented.

    I have tried to put the logic for transitioning of headings on the basis of scroll up or down event too so that the heading transition correctly in order irrespective of scroll direction.
    I am aware the code requires a lot of refactoring, but I hope it works as per the requirement.

    let headings = Array.from(document.querySelectorAll('.animated-text'));
    const sticky = document.querySelector('.sticky');
    let lastScrollTop = 0;
    let currentActive=0;
    window.addEventListener('scroll', () => {
      const stickyDiv=sticky.getBoundingClientRect(); 
      let st = window.pageYOffset || document.documentElement.scrollTop; 
      if (st > lastScrollTop) {
         if (stickyDiv.top===0) {
         for(let i=currentActive;i<headings.length-1;i++)
            {
             const headingRect = headings[i].getBoundingClientRect();
             if(headingRect.top<=0){
                    currentActive=i+1;
                    headings[i].classList.remove('active');
                    headings[i+1].classList.add('active');
             }
         }
      }
    } else if (st < lastScrollTop) {
        if (stickyDiv.top===0) {
        for(let i=currentActive;i>0;i--)
        {
         const headingRect = headings[i].getBoundingClientRect();
         if(headingRect.top<=0){
                currentActive=i-1;
                headings[i].classList.remove('active');
                headings[i-1].classList.add('active');
         }
       }
      } }
      if(stickyDiv.top>0){
            headings[currentActive].classList.remove('active');
            currentActive=0;
            headings[0].classList.add('active');
      }
       lastScrollTop = st <= 0 ? 0 : st; 
       });
    

    The full code snippet is as follows:-

    let headings = Array.from(document.querySelectorAll('.animated-text'));
    const sticky = document.querySelector('.sticky');
    let lastScrollTop = 0;
    let currentActive=0;
    window.addEventListener('scroll', () => {
      const stickyDiv=sticky.getBoundingClientRect(); 
      let st = window.pageYOffset || document.documentElement.scrollTop; 
      if (st > lastScrollTop) {
         if (stickyDiv.top===0) {
         for(let i=currentActive;i<headings.length-1;i++)
            {
             const headingRect = headings[i].getBoundingClientRect();
             if(headingRect.top<=0){
                    currentActive=i+1;
                    headings[i].classList.remove('active');
                    headings[i+1].classList.add('active');
             }
         }
      }
    } else if (st < lastScrollTop) {
        if (stickyDiv.top===0) {
        for(let i=currentActive;i>0;i--)
        {
         const headingRect = headings[i].getBoundingClientRect();
         if(headingRect.top<=0){
                currentActive=i-1;
                headings[i].classList.remove('active');
                headings[i-1].classList.add('active');
         }
       }
      } }
      if(stickyDiv.top>0){
            headings[currentActive].classList.remove('active');
            currentActive=0;
            headings[0].classList.add('active');
      }
       lastScrollTop = st <= 0 ? 0 : st; 
       });
    body {
      margin: 0
    }
    
    section {
      position: relative;
    }
    
    .sticky {
      padding-bottom: 150px;
      background: #2d232c;
      position: sticky;
      top: 0;
      overflow: hidden;
      height: auto;
      color: white;
    }
    
    .animated-text {
      opacity: 0;
      height: 0;
      overflow: hidden;
      transition: opacity 1s ease, height 1s ease, transform 1s ease;
      transform: translateY(0);
    }
    
    .animated-text.active {
      position:absolute;
      top:0;
      height: auto;
      opacity: 1;
      transform: translateY(-20px);
    }
    
    .hero, .end {
      height: 100px;
      background: white;
    }
    <section class='hero'>
      <p>Start</p>
    </section>
    
    <section class='sticky'>
      <div class='text-animations'>
        <h1 class='animated-text active'>Intro</h1>
        <h2 class='animated-text'>First</h2>
        <h2 class='animated-text'>Second</h2>
        <h2 class='animated-text'>Third</h2>
      </div>
      <p class='intro_content'>
         Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
      </p>  
    </section>
    
    <section class='end'>
      <p>End<p>
    </section>

    Please feel free to ask if any part of the logic is unclear or require clarification.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search