3

First, my HTML:

<p id="p"></p>

<input id="input" />

<button onclick="fill()">Fill</button>

Then my Javascript:

function fill() {
    var x = document.getElementById('input').value;
    var y = x.split('');
    for (var i = 0; i < y.length; i++) {
        if (i == 0) {
            setTimeout(function() {
                document.getElementById('p').innerHTML = y[i];
            },(i * 50));
        }

        setTimeout(function() {
            document.getElementById('p').innerHTML += y[i];
        },(i * 50));
    }
}

What it does is take text from a textfield, cut each character including spaces into an array, then loop through the array, displaying each character at 50ms intervals. The effect is supposed to look like it is typing itself out.

The effect works fine, but the values of the array don't seem to be. If I were to type "abc123" then I would expect that to come right back out, but instead I get:

undefinedundefinedundefinedundefinedundefinedundefined

Using console.log(), when I check the array it looks fine, when I check the typeof of the individual array values I get string, but when I check the typeof for the array, I get object. Maybe this is what's wrecking it...

I have tried use toString() on the y[i] which just spits out "[object Window]", I have tried defining the array like this var y = new Array; and then doing the split(), nothing works. I am at a complete loss. Really would love some help here. Thanks!

Eric David Sartor
  • 597
  • 2
  • 8
  • 22
  • I don't understand why you set a timeout of 0 if i equals 0 (because 50 * 0 is 0). – Sylvia Stuurman May 21 '15 at 14:28
  • @eric yuyokk's solution is apt , but i think there is a logical mistake , since the second timeout needs to be in the else block as you wanted the exact string to be echoed back. – touchStone May 21 '15 at 14:37

3 Answers3

3

I believe there must be closure problem. Try this js code. I wrapped everything inside loop in a IIFE function. I think it is well explained here Please explain the use of JavaScript closures in loops

<script>
  function fill() {
      var x = document.getElementById('input').value;
      var y = x.split('');
      for (var i = 0; i < y.length; i++) {
        (function(i){
          if (i == 0) {
              setTimeout(function() {
                  document.getElementById('p').innerHTML = y[i];
              },(i * 50));
          }

          setTimeout(function() {
              document.getElementById('p').innerHTML += y[i];
          },(i * 50));
        })(i);
      }
  }
</script>
Community
  • 1
  • 1
iurii
  • 4,142
  • 2
  • 23
  • 28
1

By calling setTimeout you're scheduling something to happen in the future. By the time the function you're delaying with setTimeout is executed, the loop has executed all the way through and i is now equal to y.length. So if you input test when the function executes it tries to add y[4] as a letter which is undefined. To fix it, you can do something like this:

function fill() {
    var x = document.getElementById('input').value;
    var y = x.split('');
    console.log(y);
    for (var i = 0; i < y.length; i++) {
        timeOutAdd(y[i], i*50)
    }
}


function timeOutAdd(c, delay){
    setTimeout(function() {
        document.getElementById('p').innerHTML += c;
    }, delay);
}
<p id="p"></p>

<input id="input" />

<button onclick="fill()">Fill</button>

By adding the timeOutAdd function which is called immediately instead of delayed we can hang on to the values of the parameters until the setTimeout function runs.

Note: I also removed the second call to setTimeout, it wasn't necessary and caused a bug where the first character of the string was output twice.

0

This solves the closure problem, plus it simplifies the logic for outputting the data. No need for splitting the string into an array:

function fill() {
  var x = document.getElementById('input').value,
      p = document.getElementById('p');
  
  for(var i = 0; i < x.length; i++) {
    (function(i) {
      setTimeout(
        function() {
          p.innerHTML= x.substr(0, i+1);
        },
        i*50
      )
    })(i);
  }
}
<p id="p"></p>

<input id="input" value="abcde" />

<button onclick="fill()">Fill</button>
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79