Running a loop to animate in a callback function

Inside the function updateMapColor, the colors of a world map change based on the input year. I have tried to animate the color change over multiple years by calling updateMapColor, but it is not working.

Do I need to incorporate setInterval? If so, could someone please explain why? What might be causing the issue?

d3.select('body').append('button').attr({
    class: "button",
    id: "animateMap"
})
    .text("Animate the map")
    .on("click", function (d) {
        for (i = 0; i < yearArray.length; i++) {
            updateMapColor(yearArray[i]);
        }
    })

var updateMapColor = function (y) {
    year = y;
    var rankingList = {};
    coloredWorldMap.transition().duration(700).attr('fill', function (d) {
        a = d;
        x = d3.values(tennis).filter(function (d) {
            return (d.DATE == year);
        });
        rankingList = x;

        x = d3.values(x).filter(function (d) {
            return (d.COUNTRY_ID == a.id);
        });

        if (x.length != 0) {
            if (x[0].COUNT == 0) {
                return "#fff";
            }
            else {
                return colorScale(x[0].COUNT);
            }
        }

        else {
            return '#fff';
        }

        return colorScale(x[0].COUNT);
    });
}

Answer №1

If you've ever tackled the challenge of calling a function with a delay inside a JavaScript for loop, you'll recognize this classic problem.

The issue at hand is quite straightforward: the for loop executes almost instantly (within milliseconds for even just a hundred iterations), causing all calls to the updateMapColor function to occur nearly simultaneously.

Luckily, there are various solutions to resolve this dilemma. One effective approach involves leveraging an Immediately Invoked Function Expression (IIFE):

(function loop(i) {
    if (i++ >= (yearArray.length - 1)) return;
    setTimeout(function() {
        updateMapColor(i);
        loop(i)
    }, 500)
})(-1);

Breaking it down:

This IIFE initiates with i = -1. Upon each iteration, i increments (via i++)—effectively starting at 0, akin to your original for loop—and evaluates whether it exceeds or matches yearArray.length - 1. If so, the loop function exits. Otherwise, a setTimeout is triggered, invoking both updateMapColor and the recursive loop function.

For a hands-on demonstration, observe the script below, operating with a setInterval duration of 0.5 seconds:

var yearArray = d3.range(20);

(function loop(i) {
    if (i++ >= (yearArray.length - 1)) return;
    setTimeout(function() {
        updateMapColor(i);
        loop(i)
    }, 500)
})(-1);

function updateMapColor(index){
console.log("The function was called with i = " + index);
}
<script src="https://d3js.org/d3.v4.min.js"></script>

PS: For a smoother solution, consider implementing a series of D3 transitions.

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

AngularFire and Ionic - There is no data being sent to the server from the form

I am using Ionic and AngularFire to build my app, but I have encountered a new issue: the form data is empty! Only the fields in my Firebase database are visible. How can I retrieve the user-entered form data from the Firebase database? Here is a screensh ...

Enhancing React Functionality: Increasing React State Following an If Statement

Whenever I click on the start/stop button, it triggers the handlePlay function. This function then proceeds to initiate the playBeat function. In an ideal scenario, the play beat function should continuously display 1222122212221222... until I press the st ...

JavaScript keydown event for rotating images

I am experiencing an issue with JavaScript animation. I have incorporated code from this particular link into my keydown function. However, the code is not functioning as expected. While the code from the provided link works fine on its own, within the key ...

Using Previous and Next Buttons in Bootstrap Tabs for Form Navigation

I am currently facing a challenge in splitting a form into multiple tabs and encountering issues with the next and previous buttons on the second and third tabs. My goal is for the tabs at the top to change state as you cycle through them by clicking the ...

Navigating to a different state key within a state object in React - a simple guide

Trying to dive into React, I encountered an issue. My goal is to create my own example from a tutorial. import React, { Component } from 'react'; class MyComponent extends Component { state = { persons: [], firstPersons: 5, variab ...

How come defining the state using setTimeout does not display the accurate properties of a child component?

Presented here is a component designed to render a list of items and include an input for filtering. If no items are present or if the items are still loading, a message should be displayed. import { useState } from "react"; export const List = ...

The property this.props.Values is not defined

I'm facing an issue with a page. Specifically, I am working with the value: this.props.CategoriesList. This value represents a list of categories. The problem is that when I click on a button to navigate to the page where this value is used, it shows ...

The animation unexpectedly resets to 0 just before it begins

Currently, I am developing a coverflow image slider with jQuery animate. However, there are two issues that I am facing. Firstly, when the animation runs for the first time, it starts at `0` instead of `-500`. Secondly, after reaching the end and looping b ...

Ways to avoid scrolling on a fixed element

Here is the HTML setup I have... body .top .content The issue I am facing is that when scrolling reaches the end of the ul in the .top element, the background starts to scroll. This can be quite disorienting and makes the site slow on tablets. Even ...

ReactJs: difficulty in resetting input field to empty string

I have an application using React v 0.13.1 (Old version). I am struggling to update my input field to "" after retrieving the updated value from the database. Scenario: I am updating the input fields by clicking on the button named "Pull&qu ...

What's with the increased verbosity of mongoose query result objects in the latest version?

After installing my dependencies on a new computer, I noticed that mongoose must have been updated. The informative results from my queries are now appearing in messy outputs with excessive information that is not always useful. It used to be concise and c ...

The most efficient method of assigning array indexes to boolean values within an object

Struggling to determine the most efficient method of assigning array indices to an object with boolean values. Currently employing the following approach: let arr = [{t:1, te: "aa"}, {t:2, te: "aa"},{t:2, te: "aa"} ]; let obj ...

Streaming data from MongoDB to a file using Node.js

Having recently delved into the world of javascript/node.js, I am attempting to accomplish a basic task: connect to MongoDB, convert the JSON response to CSV format, and then write it to a file. My current approach is outlined below: fs = require('fs ...

Is it viable to create an onClick event for the text content within a text area?

I'm working on a project that involves displaying XML data in a textarea and creating an onClick event for the content within the textarea. For example: <textarea>Hello Web, This is a simple HTML page.</textarea> My goal here is to cre ...

I possess a webpage containing a div element that is loaded dynamically through ajax

One issue I'm facing is with a page containing a div that gets loaded via ajax when a button is clicked. The problem arises when a user tries to refresh the page by pressing F5, as the content of the div gets lost! Is there a way to ensure that when ...

Experience the impact of a lagging network connection on Chrome extensions through the power of React-NextJS

I'm currently working on a Chrome extension that requires me to limit download speeds in the browser programmatically (client-side). Despite researching extensively on the subject, I have not been able to find any information on how to achieve this us ...

Issues with updating $setValidity from a directive in AngularJS

My current challenge involves building a custom directive (inspired by an example I came across) to ensure that the confirm password matches the initial password input. Even though all my console.log() statements are executing and displaying, it seems like ...

Ways to retrieve the value of a variable beyond its scope while using snapshot.foreach

I am experiencing an issue where the return statement returns a null value outside the foreach loop of the variable. I understand that the foreach loop creates its own scope, but I need to figure out how to return the value properly... this.selectedUserMe ...

Issue with triggering onchange action for Multiple File Upload functionality

I'm currently in the process of developing a Website that requires a feature allowing users to upload multiple files. Here is the input-field I am working with: <div class="carousel-item carousel-custom-item active input-wrapper" > <inpu ...

"Scotchy McScotchface's to-do list application powered

How is the index.html (frontend Angular) being triggered? The tutorial mentioned that by including one of the following routes in route.js, the frontend gets called app.get('*', function(req, res) { res.sendfile('./public/index.html&ap ...