Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I animate DOM elements in a loop with interval between each iteration?

Tags:

javascript

I have a function foo and I wanted to add a sleep/wait function to make a kind of DOM elements animation. I've already done some research and I know that it's impossible to pause a javascript function because it freezes browser - correct me if I'm wrong. How can I overcome it?

function foo() {     
 while (someCondition) {
  var $someDiv = $('.someDiv:nth-child(' + guess + ')');
  $someDiv.css({'background-color': 'red'});
  wait 1000ms
  $someDiv.css({'background-color': 'blue'});
  wait 1000ms
  if (someCondition2) { 
   doSomething; }
  else {
   for loop }
 }
}

$someDiv refers to different DOM element with each while loop iteration because variable guess changes

What I've tried

  • I used the function below and it worked but the problem is I couldn't use a for loop in my async function foo

    function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
    }
    
  • I tried setTimeout but I wasn't able to achieve any valid result.

    If I wrap in setTimeout this piece of code: ('$someDiv').css({'background-color': 'red'}); then after specified amount of time all $someDiv's change css style together (keep in mind that $someDiv refers to different DOM element with each while loop iteration).

    If I wrap in setTimeout a piece of code with if, else statements then I've got an error - Infinite Loop

Question

The foo function is simplified just for visualisation the problem. The original function I'm working on you can find on codepen (findNumber function)

I want to make a binary search algorithm animation. Something similar to this

How can I achieve the desired result?

In general: How can I animate DOM elements in a loop with interval between each iteration?

like image 307
Arkej Avatar asked Dec 22 '16 11:12

Arkej


1 Answers

The nicest, cleanest solution to this problem is with the async/await feature that will come in a future version of Javascript (ES2017). This allows you to get out of callback hell. You can create a simple sleep function that looks like this:

function sleep(time) {
  return new Promise(resolve => setTimeout(()=>resolve(), time));
}

You could use this with normal Promise handling:

sleep(1000).then(()=>console.log('A second later'));

However, with the async functionality you can use the await keyword to make the code wait for the promise to be resolved before continuing.

async function doSomething() {
  await sleep(1000);
  console.log('A second later');
}

This means that you can use a normal loop as well, including break and continue statements:

async function doSomething() {
  let i = 0;
  while (true) {
    await sleep(1000);
    console.log(i);
    if (++i === 5) break;
  }
}

This means that your code can be dramatically simplified:

async function foo() {
  var n = 5;
  while (n > 0) {
    n--;
    var wait = 0;
    //$('#someDiv').css({'background-color': 'red'});
    console.log('doSomething-1');
    //wait 1000ms
    await sleep(1000);
    //$('#someDiv').css({'background-color': 'blue'});
    console.log('doSomething-2');

    //wait 1000ms
    await sleep(1000);
    if (true) {
      console.log('doSomething-3');
      break;
    } else {
      console.log('loop')
    }
  }
}

(jsFiddle)

The only problem is that this functionality has far from universal support. You therefore need to transpile it using software like Babel.

Note also that, behind the scenes, your foo function now returns immediately and gives a Promise. That Promise is resolved with the return value of the function. So if you wanted to do more code when foo was completed, you would have to do foo().then(/*callback*/).

like image 97
lonesomeday Avatar answered Oct 23 '22 03:10

lonesomeday