2

I am trying to write a javascript program which stores the value from an input element in an array when a button is clicked. The array is the split and each individual letter added to a span element and then appended to the document. The idea is to create a typing effect using setTimeout.

I am running into an issue creating a closure within the loop, so currently the setTimeout function always returns the final value of the iteration.

The function in question is at the bottom of the code block and called addTextToBoard();

var noteButton = document.querySelector('[data-js="button"]');

noteButton.addEventListener("click",function() {
  var messageIn = document.querySelector('[data-js="input"]');
  var message = messageIn.value;
  postToBoard(message);
});

function postToBoard(val) {
  var noteBoard = document.querySelector('[data-js="noteboard"]');
  var newElement = document.createElement('div');
  newElement.classList.add('noteboard__item');
  noteBoard.appendChild(newElement);
  setTimeout(function(){
    newElement.classList.add('active');
  }, 200);
  addTextToBoard(newElement, val);
}

function addTextToBoard(el, val) {
   var wordArray = val.split('');
   for(i = 0; i < wordArray.length; i++) {
       var letter = document.createElement('span');
       letter.innerHTML = wordArray[i];
       setTimeout(function(x){
         return function() {}
         el.appendChild(letter);
       }(i),1000);
   }
}

I believe I am close, I'm just not fully understanding the syntax for creating the closure. If someone could give poke in the right direction, without necessarily giving the full solution that would be great.

I essentially tried to paste in the following code snippet from here but I've missed something somehwere along the way!

setTimeout(function(x) { return function() { console.log(x); }; }(i), 1000*i);

Best,

Jack

Sergio Moura
  • 4,888
  • 1
  • 21
  • 38
Jakkc
  • 21
  • 1
  • 4
  • Maybe [this question](https://stackoverflow.com/questions/25266904/javascript-closure-not-working) helps you with fixing the closure – Bergi Jun 18 '16 at 12:28

3 Answers3

1

You are close.

Since the "letter" variable changes, you'll add only the last letter over and over again. You need to "save" the current letter on the setTimeout() callback function, One way to go is like this:

function appendMyLetter(letter) {
  return(function() {
    el.append.Child(letter);
  });
}

function addTextToBoard(el, val) {
   var wordArray = val.split('');
   for(i = 0; i < wordArray.length; i++) {
       var letter = document.createElement('span');
       letter.innerHTML = wordArray[i];
       setTimeout(appendMyLetter(letter), 1000);
   }
}

This way, the appendMyLetter() function gets called with a different parameter (one for each letter) and returns a function with the correct "stored" value to be called by setTimeout().

EDIT

Looking at your setTimeout() code closely

setTimeout(function(x){
  return function() {}
    el.appendChild(letter);
}(i),1000);

It would work fine, if you used the proper parameters and used the appendChild() inside the returned function, like so:

setTimeout(function(x){
  return(function() {
    el.appendChild(x);
  });
}(letter),1000);
Sergio Moura
  • 4,888
  • 1
  • 21
  • 38
0

You can create an immediately-invoked function expression IIFE to create a closure

 function addTextToBoard(el, val) {
       var wordArray = val.split('');
       for(i = 0; i < wordArray.length; i++) {
          (function(index) {
            var letter = document.createElement('span');
             letter.innerHTML = wordArray[i];
            setTimeout(function(){
             el.appendChild(letter);
            },1000);
        })(i);
        }
    }
brk
  • 48,835
  • 10
  • 56
  • 78
0

I dont know if this will work but here you go a slight change in operator:

letter.innerHTML += wordArray[i];

if you dont get the effect you imagined you will get you better try to increment the timer by i like this

setTimeout(function(){
 ...
},1000*i);
Shrikantha Budya
  • 646
  • 1
  • 4
  • 15