Tips for ensuring the counter displays numbers incrementally instead of all at once

Is it possible to make the counters count sequentially (from left to right) instead of all at the same time? Here is the code for my counter:

(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);
      }
    });
  };

 …(remaining code truncated for brevity)
    
</div>

Appreciate any assistance provided

Answer №1

It seems more straightforward to use recursion rather than waiting for one process to finish before starting another. I replaced the jquery .each with a vanilla querySelector because it returns the first element that matches 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>

Answer №2

Utilizing the .queue(), .dequeue(), .next() functions in jQuery is essential for executing your counters in a sequential manner. Check out this example for a similar implementation, and see how you can integrate it into your code:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Deactivate hover effects and media queries for Material UI controls in all states

Since I am using a touch-capable monitor, I noticed that hover styles are not showing up on any controls. Specifically, when I hover over a Material UI button, I see the following styles: https://i.stack.imgur.com/uwBpt.png Is there a way to disable all ...

What is the best way to iterate through a JSON associative array using JavaScript?

When I receive a JSON response from the server, my goal is to loop through the array in JavaScript and extract the values. However, I am facing difficulties in doing so. The structure of the JSON response array is as follows: { "1": "Schools", "20" ...

The baffling quirks of variables within a Jquery loop

Unfortunately, I'm struggling to come up with a more fitting title for my question, but I'll do my best to provide a clear explanation of my issue. Here is the code snippet I am working with: let pdfInvoice_sub_template = [ {text: '{ ...

The Angular Material Table experienced a collapse when trying to render over 20 columns simultaneously

Currently, I am experiencing an issue in Angular Version 5 where the Angular Material Table collapses when rendering more than 20 columns. Here is a snapshot of what my table looks like: https://i.stack.imgur.com/MXfvQ.png https://i.stack.imgur.com/XHWgq ...

The CSS theme toggler for Bootstrap

I am currently working on integrating a style switcher following the instructions provided at . However, when I add a title="" attribute to the CSS link, the CSS file fails to load on the page and the styles revert back to default Bootstrap. I have added ...

Adaptable arrow-shaped progress bar featuring sleek transparent borders

I am currently working on creating a progress bar similar to those commonly found in checkout processes. One issue I have encountered is that the borders between the arrows appear transparent, and the entire design needs to be responsive. So far, I have m ...

Struggling with extracting an array of objects from a JSON file and accessing individual attributes within each object?

As a newcomer to Typescript, I am eager to work with JSON and access its objects and attributes. However, I am encountering difficulties in achieving this. I have attempted using functions like 'for of' and 'for in', but unfortunately, ...

What is causing the high data usage of 12kB/minute when I am only writing data to the Firebase Database?

Utilizing the native Firebase Javascript SDK's on my IoT device running Node-red environment has been a seamless process. My code primarily focuses on WRITE and DELETE operations within the Firebase RealtimeDatabase data. The connection is establishe ...

Dynamic, adaptable, and expandable tiles that effortlessly span the full width of the screen

Trying to design fluid and flexible "tiles" for a website, which should expand across 100% of the browser viewport. The goal is for them to adjust in size if necessary to fill any white space gaps left by unfitted tiles. Using a standard div tag with a mi ...

Stacking background images in CSS above other contained elements in a div

I've been experimenting with adjusting the z-index of elements in my code but haven't had any luck. Is it even possible to achieve what I want? Within a div element, I have set a background image using CSS. Inside this div, there are other eleme ...

Changing the class of an element using CSS when hovering over another

Is it possible to dynamically change the style of another element when hovering over a specific element? For example, I want a menu item to change its appearance when hovering over a submenu item. Here is the CSS code I have: ul.menu .menulink { padding ...

Creating a POST Endpoint in Express JS

Hey there! Can someone help me out with creating a basic login script for an app using Express JS? I've been working on a POST function to handle this task, but unfortunately, when I try to echo back the parameters being passed (testing via Postman), ...

Tips for integrating SQL queries into a document that consists mostly of JavaScript and JQuery

I am currently in the process of integrating a SQL database write into my file that is primarily comprised of JavaScript and jQuery. While I have come across some PHP resources online, I am facing challenges incorporating the PHP code into my existing scri ...

Navigating through Angular JS validation procedures step by step

I have a wizard in angular js consisting of multiple steps: <wizard on-before-step-change="log(event)" on-step-changing="log(event)" on-after-step-change="log(event)" user="user"> <step title="step 1"> </step> <step title="step 2"& ...

What is the best way to build a Div structure using a JavaScript Array?

I am currently attempting to create a simple div construct based on a JavaScript array. However, my current approach only displays the last group/element of the array. What adjustments need to be made in order to generate a repeating div construct for each ...

Troubleshooting issue with jQuery animate not correctly scrolling

I am experiencing an issue with my jQuery code that is supposed to scroll a smaller container element within a larger container element when a button is clicked. Despite testing with an alert and verifying that the code is working, the scrolling functional ...

Track the amount of time a particular user spends on the website and save the data in a MySQL database

I am looking to track the amount of time each user spends on my page in seconds. For example, if User X enters the site at 8:00 am and leaves at 8:15 am, I want to add 900 seconds to their account. Let's say the user has visited multiple times before ...

Angular4 allows the creation of new rows when products are added to a carousel component

Currently, I am working on an Angular4 application that includes a carousel displaying popular products. At the moment, the default view shows 3 products, and clicking on the left or right buttons reveals another set of 3 products. The total static values ...

Updating visual appearance with button clicks and unclicks

Is there a way to dynamically update the button image while clicking on it? This is what I have tried: $('.gamebox_minimap_plus').click(function() { $(this).css("background-image","url('gfx/plus2.png')"); }); The image ch ...

Tips for transferring PHP variable from a drop down menu

Hello, I am currently working on creating a dropdown menu using the <select> tag. My goal is to have it so that when someone clicks on one of the options in the dropdown, a new window opens. Additionally, I want the PHP variable from the main page to ...