2

I'm trying to make a text display in DOM from a->b->c->d->e with a 1-second delay between each alphabet using setTimeout. However, the result came out as letter changing from 'a' an immediately to 'e' without displaying 'b' 'c' 'd'. However, all the letters are displayed in the console without having 1-second delay. Sorry for my confusing explanation.

function changeLetter() {
  const symbols = ['Ai', 'a', 'b', 'c', 'd']
  let symbol
  for (let i = 0; i < symbols.length; i++) {
    setTimeout(() => {
      symbol = symbols[i]
      console.log(symbol)
      document.getElementById("change").innerHTML = symbol;
    }, 1000)
  }
}
changeLetter()
<h1 id="change"></h1>
Aniket G
  • 3,471
  • 1
  • 13
  • 39
Kittichote Chain
  • 596
  • 1
  • 10
  • 22

3 Answers3

1

This is happening as all the setTimeout() calls are registered at 1000 ms and overlapped, so you see the transition to d happening instantaneously and hence skipping the elements in between.

You need to register the setTimeout() for the next elements consecutively. So simply 1000 * (i + 1) will do this in the setTimeout call. For the first element it will be 1000 ms, the next will be at 2000 ms and so on.

function changeLetter() {
  const symbols = ['Ai', 'a', 'b', 'c', 'd']
  let symbol
  for (let i = 0; i < symbols.length; i++) {
    setTimeout(() => {
      symbol = symbols[i]
      console.log(symbol)
      document.getElementById("change").innerHTML = symbol;
    }, 1000 * (i + 1))
  }
}
changeLetter()
<h1 id="change"></h1>
Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
1

The problem is that the entire for loop runs before the first timeout can fire.

An elegant way to handle this is to call the function from the timeout itself. This way the next timeout doesn't begin until the previous has finished (this uses % symbols.length to make it loop continuously):

const symbols = ['Ai', 'a', 'b', 'c', 'd']

function changeLetter(i) {
    setTimeout(() => {
        symbol = symbols[i]
        document.getElementById("change").innerHTML = symbols[i];
        changeLetter((i+1) % symbols.length) 
    }, 1000)
}


changeLetter(0)
<h1 id="change"></h1>

Alternatively, you could just use setInterval

const symbols = ['Ai', 'a', 'b', 'c', 'd']
count = 0
let inthandle = setInterval(() => {
  document.getElementById("change").innerHTML = symbols[count];
  count = (count + 1) % symbols.length
}, 1000)
<h1 id="change"></h1>
Mark
  • 90,562
  • 7
  • 108
  • 148
0

The issue is that your for loop executes immediately. So you're instantiating 5 setTimeout calls simultaneously, which all execute at the same time. Since the last setTimeout's callback changes the value to d, you're seeing that immediately.

Instead, you'll want to recursively delay calling each setTimeout until the previous one completed. Something like this:

const symbols = ['a','b','c'];
const currentSymbol = 0;

function changeLetter() {
  if (currentSymbol < symbols.length) {
    document.getElementById("change").innerHTML = symbols[currentSymbol];
    currentSymbol++;
    setTimeout(changeLetter, 1000);
  }
}

benjarwar
  • 1,794
  • 1
  • 13
  • 28