Home > Software design >  Counter Observe when I scroll
Counter Observe when I scroll

Time:12-03

Problem

I created a counter using HTML, CSS and JS (such as satisfied customer numbers, branch numbers, etc.) The counter is also animated but since it's down the page, I'd like to animate it only when it gets to that point on the page. How do I do with the js?

const counters = document.querySelectorAll('.value');
const speed = 400;

counters.forEach( counter => {
   const animate = () => {
      const value =  counter.getAttribute('akhi');
      const data =  counter.innerText;
     
      const time = value / speed;
     if(data < value) {
          counter.innerText = Math.ceil(data   time);
          setTimeout(animate, 1);
        }else{
          counter.innerText = value;
        }
     
   }
   
   animate();
});
.counter-box {

    display: block;
    background: #f6f6f6;
    padding: 40px 20px 37px;
    text-align: center

}
.counter-box p {

    margin: 5px 0 0;
    padding: 0;
    color: #909090;
    font-size: 18px;
    font-weight: 500

}

.counter { 

    display: block;
    font-size: 32px;
    font-weight: 700;
    color: #666;
    line-height: 28px

}
.counter-box.colored {

      background: #eab736;

}
.counter-box.colored p,
.counter-box.colored .counter {

    color: #fff;

}
        <div >
          <div >
            <div >
              <div >
                <span  akhi="560">0</span>
                   <p>Countries visited</p>
              </div>
            </div>

    <div >
       <div >
           <span  akhi="3275">0</span>
              <p>Registered travellers</p>
       </div>
    </div>

    <div >
        <div >
            <span  id="conta" akhi="289">0</span>
               <p>Partners</p>
        </div>
    </div>
         </div> 
        </div>

What I have tried

i had tried with

const target = document.querySelector('.counter');
observer.observe(target);

but it doesn't seem to work. Many thanks to whoever can help me.

CodePudding user response:

I would recommend, as others have suggested, to use the Intersection Observer API to animate your elements once they appear in the viewport.

The idea is simple, we'll create an observer that will observe the counters to animate and we're going to configure it so that it calls the animate function once a counter is fully visible in the viewport.

You may learn more about the options that an IntersectionObserver can accept in order to customize its behavior. Meanwhile, here's a live demo that illustrates how to make the counters animate once they appear in the screen (the code below has some helpful comments):

const counters = document.querySelectorAll('.value'),
  speed = 400,
  /**
   * create an IntersectionObserver with the specified callback that will be executed for each intersection change for every counter we have. 
   * You may customize the options (2nd argument) per you requirement
   */
  observer = new IntersectionObserver(
    entries => entries.forEach(entry => entry.isIntersecting && animate(entry.target)), 
    {
      threshold: 1 // tells the browser that we only need to execute the callback only when an element (counter) is fully visible in the viewport
    }
  ),
  // the animate function now accepts a counter (HTML element)
  animate = counter => {
    const value =  counter.dataset.akhi,
      data =  counter.innerText,
      time = value / speed;
    if (data < value) {
      counter.innerText = Math.ceil(data   time);
      setTimeout(() => animate(counter), 1);
    } else {
      counter.innerText = value;
    }
  };

// attach the counters to the observer
counters.forEach(c => observer.observe(c));
.counter-box {
  display: block;
  background: #f6f6f6;
  padding: 40px 20px 37px;
  text-align: center
}

.counter-box p {
  margin: 5px 0 0;
  padding: 0;
  color: #909090;
  font-size: 18px;
  font-weight: 500
}

.counter {
  display: block;
  font-size: 32px;
  font-weight: 700;
  color: #666;
  line-height: 28px
}

.counter-box.colored {
  background: #eab736;
}

.counter-box.colored p,
.counter-box.colored .counter {
  color: #fff;
}
<div >
  <div >
    <div >
      <div >
        <!-- it is recommended to use "data-*" attributes to cache data that we might use later. The "data-akhi" contains the number to animate -->
        <span  data-akhi="560">0</span>
        <p>Countries visited</p>
      </div>
    </div>
    <div >
      <div >
        <span  data-akhi="3275">0</span>
        <p>Registered travellers</p>
      </div>
    </div>
    <div >
      <div >
        <span  id="conta" data-akhi="289">0</span>
        <p>Partners</p>
      </div>
    </div>
  </div>
</div>

CodePudding user response:

As others suggested, you should use Intersection Observer. This is how I'd do: Scrolldown the snippet in order to see the counter animating up once is on the screen.

const counters = document.querySelectorAll('.value');
const speed = 400;

const observer = new IntersectionObserver( items => {
    
  if(items[0].isIntersecting) { 
    const target = items[0].target;
    const animate = () => {
      const value =   target.getAttribute('akhi');
      const data =   target.innerText;
     
      const time = value / speed;
      if(data < value) {
          target.innerText = Math.ceil(data   time);
          setTimeout(animate, 1);
      }else{
          target.innerText = value;
      }     
   }   
   animate(); 
   observer.unobserve(target);
  }
})

counters.forEach( counter => observer.observe(counter));
.counter-box {

    display: block;
    background: #f6f6f6;
    padding: 40px 20px 37px;
    text-align: center

}
.counter-box p {

    margin: 5px 0 0;
    padding: 0;
    color: #909090;
    font-size: 18px;
    font-weight: 500

}

.counter { 

    display: block;
    font-size: 32px;
    font-weight: 700;
    color: #666;
    line-height: 28px

}
.counter-box.colored {

      background: #eab736;

}
.counter-box.colored p,
.counter-box.colored .counter {

    color: #fff;

}
<div style="height: 600px;">

</div>


<div >
          <div >
            <div >
              <div >
                <span  akhi="560">0</span>
                   <p>Countries visited</p>
              </div>
            </div>

    <div >
       <div >
           <span  akhi="3275">0</span>
              <p>Registered travellers</p>
       </div>
    </div>

    <div >
        <div >
            <span  id="conta" akhi="289">0</span>
               <p>Partners</p>
        </div>
    </div>
         </div> 
        </div>

  • Related