2

I built a counter to count up to a target number. All the four counter are counting at the same time. Is there any possibility that (from left to right) that the first counter counts, when it is finished then the second counter counts etc., respectively not all at the same time? Here is my code:

(function($) {
  $.fn.countTo = function(options) {
    options = options || {};

    return $(this).each(function() {
      // set options for current element
      var settings = $.extend({}, $.fn.countTo.defaults, {
        from: $(this).data('from'),
        to: $(this).data('to'),
        speed: $(this).data('speed'),
        refreshInterval: $(this).data('refresh-interval'),
        decimals: $(this).data('decimals')
      }, options);

      // how many times to update the value, and how much to increment the value on each update
      var loops = Math.ceil(settings.speed / settings.refreshInterval),
        increment = (settings.to - settings.from) / loops;

      // references & variables that will change with each update
      var self = this,
        $self = $(this),
        loopCount = 0,
        value = settings.from,
        data = $self.data('countTo') || {};

      $self.data('countTo', data);

      // if an existing interval can be found, clear it first
      if (data.interval) {
        clearInterval(data.interval);
      }
      data.interval = setInterval(updateTimer, settings.refreshInterval);

      // initialize the element with the starting value
      render(value);

      function updateTimer() {
        value += increment;
        loopCount++;

        render(value);

        if (typeof(settings.onUpdate) == 'function') {
          settings.onUpdate.call(self, value);
        }

        if (loopCount >= loops) {
          // remove the interval
          $self.removeData('countTo');
          clearInterval(data.interval);
          value = settings.to;

          if (typeof(settings.onComplete) == 'function') {
            settings.onComplete.call(self, value);
          }
        }
      }

      function render(value) {
        var formattedValue = settings.formatter.call(self, value, settings);
        $self.html(formattedValue);
      }
    });
  };

  $.fn.countTo.defaults = {
    from: 0, // the number the element should start at
    to: 0, // the number the element should end at
    speed: 1000, // how long it should take to count between the target numbers
    refreshInterval: 100, // how often the element should be updated
    decimals: 0, // the number of decimal places to show
    formatter: formatter, // handler for formatting the value before rendering
    onUpdate: null, // callback method for every time the element is updated
    onComplete: null // callback method for when the element finishes updating
  };

  function formatter(value, settings) {
    return value.toFixed(settings.decimals);
  }
}(jQuery));

jQuery(function($) {
  // custom formatting example
  $('.count-number').data('countToOptions', {
    formatter: function(value, options) {
      return value.toFixed(options.decimals).replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
    }
  });

  // start all the timers
  $('.timer').each(count);

  function count(options) {
    var $this = $(this);
    options = $.extend({}, options || {}, $this.data('countToOptions') || {});
    $this.countTo(options);
  }
});
.counter {
  background-color: #f5f5f5;
  padding: 20px 0;
  border-radius: 5px;
}

.count-title {
  font-size: 40px;
  font-weight: normal;
  margin-top: 10px;
  margin-bottom: 0;
  text-align: center;
}

.count-text {
  font-size: 13px;
  font-weight: normal;
  margin-top: 10px;
  margin-bottom: 0;
  text-align: center;
}

.fa-2x {
  margin: 0 auto;
  float: none;
  display: table;
  color: #4ad1e5;
}
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->


<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
<div class="container">
  <div class="row">
    <br/>
    <div class="col text-center">
      <h2>Bootstrap 4 counter</h2>
      <p>counter to count up to a target number</p>
    </div>



  </div>
  <div class="row text-center">
    <div class="col">
      <div class="counter">
        <i class="fa fa-code fa-2x"></i>
        <h2 class="timer count-title count-number" data-to="100" data-speed="1500"></h2>
        <p class="count-text ">Our Customer</p>
      </div>
    </div>
    <div class="col">
      <div class="counter">
        <i class="fa fa-coffee fa-2x"></i>
        <h2 class="timer count-title count-number" data-to="1700" data-speed="1500"></h2>
        <p class="count-text ">Happy Clients</p>
      </div>
    </div>
    <div class="col">
      <div class="counter">
        <i class="fa fa-lightbulb-o fa-2x"></i>
        <h2 class="timer count-title count-number" data-to="11900" data-speed="1500"></h2>
        <p class="count-text ">Project Complete</p>
      </div>
    </div>
    <div class="col">
      <div class="counter">
        <i class="fa fa-bug fa-2x"></i>
        <h2 class="timer count-title count-number" data-to="157" data-speed="1500"></h2>
        <p class="count-text ">Coffee With Clients</p>
      </div>
    </div>
  </div>
</div>

Thank you for any help

secan
  • 2,622
  • 1
  • 7
  • 24
Todd
  • 27
  • 1
  • 5
  • 3
    You forgot to include your JS code. – secan Jun 09 '21 at 13:58
  • Does this answer your question? [How to animate Bootstrap 4 cards one by one?](https://stackoverflow.com/questions/51093212/how-to-animate-bootstrap-4-cards-one-by-one) – Peter Krebs Jun 09 '21 at 14:01
  • js code is in the codepen link – Todd Jun 09 '21 at 14:11
  • 1
    codepen is a fine backup solution, but it's important to include all the relevant information about your question *in the question*. External links go stale, which can leave the question useless for future users. (It's an especially odd choice to include the CSS, which is irrelevant to the question, but leave the js out.) – Daniel Beck Jun 09 '21 at 17:18

2 Answers2

1

Using recursion seems easier than trying to await one to finish etc. I did replace your jquery .each with a vanilla querySelector because that gives back the first element matching the id.

(function ($) {
    $.fn.countTo = function (options) {
        options = options || {};
        
        $(this).each(function () {
            // set options for current element
            var settings = $.extend({}, $.fn.countTo.defaults, {
                from:            $(this).data('from'),
                to:              $(this).data('to'),
                speed:           $(this).data('speed'),
                refreshInterval: $(this).data('refresh-interval'),
                decimals:        $(this).data('decimals')
            }, options);
            
            // how many times to update the value, and how much to increment the value on each update
            var loops = Math.ceil(settings.speed / settings.refreshInterval),
                increment = (settings.to - settings.from) / loops;
            
            // references & variables that will change with each update
            var self = this,
                $self = $(this),
                loopCount = 0,
                value = settings.from,
                data = $self.data('countTo') || {};
            
            $self.data('countTo', data);
            
            // if an existing interval can be found, clear it first
            if (data.interval) {
                clearInterval(data.interval);
            }
            data.interval = setInterval(updateTimer, settings.refreshInterval);
            
            // initialize the element with the starting value
            render(value);
            
            function updateTimer() {
                value += increment;
                loopCount++;
                
                render(value);
                
                if (typeof(settings.onUpdate) == 'function') {
                    settings.onUpdate.call(self, value);
                }
                
                if (loopCount >= loops) {
                    // remove the interval
                    $self.removeData('countTo');
                    clearInterval(data.interval);
          
                    value = settings.to;
                    if (typeof(settings.onComplete) == 'function') {
                        settings.onComplete.call(self, value);
                    }
          //Introduce recursion here
          const el = document.querySelector('.timer');
          el && count(el);
                }
            }
            
            function render(value) {
                var formattedValue = settings.formatter.call(self, value, settings);
                $self.html(formattedValue);
            }
        });
    };
    
    $.fn.countTo.defaults = {
        from: 0,               // the number the element should start at
        to: 0,                 // the number the element should end at
        speed: 1000,           // how long it should take to count between the target numbers
        refreshInterval: 100,  // how often the element should be updated
        decimals: 0,           // the number of decimal places to show
        formatter: formatter,  // handler for formatting the value before rendering
        onUpdate: null,        // callback method for every time the element is updated
        onComplete: null       // callback method for when the element finishes updating
    };
    
    function formatter(value, settings) {
        return value.toFixed(settings.decimals);
    }
}(jQuery));

jQuery(function ($) {
  // custom formatting example
  $('.count-number').data('countToOptions', {
    formatter: function (value, options) {
      return value.toFixed(options.decimals).replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
    }
  });
  
  // start all the timers
  //$('.timer').each(data.collection.slice(1),count);
  
  
});
//instead of the for loop above kick it off by calling count once. then rely on recursion
count(document.querySelector('.timer'));
function count(el) {
    var $this = $(el);
    el.classList.remove('timer');
    options = $.extend({}, $this.data('countToOptions') || {});
    $this.countTo(options);
  }
.counter {
    background-color:#f5f5f5;
    padding: 20px 0;
    border-radius: 5px;
}

.count-title {
    font-size: 40px;
    font-weight: normal;
    margin-top: 10px;
    margin-bottom: 0;
    text-align: center;
}

.count-text {
    font-size: 13px;
    font-weight: normal;
    margin-top: 10px;
    margin-bottom: 0;
    text-align: center;
}

.fa-2x {
    margin: 0 auto;
    float: none;
    display: table;
    color: #4ad1e5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->


<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
<div class="container">
    <div class="row">
        <br/>
       <div class="col text-center">
        <h2>Bootstrap 4 counter</h2>
        <p>counter to count up to a target number</p>
        </div>
        
             
        
    </div>
        <div class="row text-center">
            <div class="col">
            <div class="counter">
      <i class="fa fa-code fa-2x"></i>
      <h2 class="timer count-title count-number" data-to="100" data-speed="1500"></h2>
       <p class="count-text ">Our Customer</p>
    </div>
            </div>
              <div class="col">
               <div class="counter">
      <i class="fa fa-coffee fa-2x"></i>
      <h2 class="timer count-title count-number" data-to="1700" data-speed="1500"></h2>
      <p class="count-text ">Happy Clients</p>
    </div>
              </div>
              <div class="col">
                  <div class="counter">
      <i class="fa fa-lightbulb-o fa-2x"></i>
      <h2 class="timer count-title count-number" data-to="11900" data-speed="1500"></h2>
      <p class="count-text ">Project Complete</p>
    </div></div>
              <div class="col">
              <div class="counter">
      <i class="fa fa-bug fa-2x"></i>
      <h2 class="timer count-title count-number" data-to="157" data-speed="1500"></h2>
      <p class="count-text ">Coffee With Clients</p>
    </div>
              </div>
         </div>
</div>
tstrand66
  • 968
  • 7
  • 11
0

I think you need to include .queue(), .dequeue(), .next() function of jQuery to run your counters sequentially. Below is the similar kind of implementation, try and plug it with your code:

https://stackoverflow.com/a/37234451/4152622

Akash Kriplani
  • 162
  • 1
  • 12