Naren Singh
Naren Singh

Reputation: 347

Trying to make append text letter by letter

Hi I'm a newbie with JavaScript and Jquery. What I am trying to do here is to print the text passed in the onclick() function alphabet by alphabet using the setTimeout function. I'm not sure what i'm doing wrong. Thanks for your help.

HTML

<div>
         <p id='myTxt'></p>
</div>
 <input type='button' value='Submit' onclick="imScrolling('Hello World!!', 500, 0, document.getElementById('myTxt'))">   

My Script.js file

 function imScrolling(scrollTxt, interval, index, target)
    {
        //alert(scrollTxt + " " + interval + " " + scrollTxt.length + " " + index + " " + target.id);
       while(index < scrollTxt.length)
      {
         //alert(scrollTxt[index]);
         setTimeout(function (scrollTxt, interval, index, target)
                {
                   $('#myTxt').append(scrollTxt[index]);
                   }, interval);
         index++;       
       }
    }

Also, I have noticed that if I do not pass the parameters to the setTimeout(), then the parameters like index, interval are shown as undefined in the alert message? Why is it so?

Upvotes: 0

Views: 506

Answers (5)

Justin K
Justin K

Reputation: 138

Don't use setTimeout. What you want is setInterval.

function imScrolling(scrollTxt, interval, index, target) {
    //alert(scrollTxt + " " + interval + " " + scrollTxt.length + " " + index + " " + target.id);

    var notawhileloop = setInterval(function () {
        //alert(scrollTxt[index]);
        $('#myTxt').append(scrollTxt[index]);
        index++;
        if (index == scrollTxt.length) {
            clearInterval(notawhileloop);
        }
    }, interval);
}

Working jsfiddle with your HTML.

Edit: Oops, forgot to stop the looping. Updated fiddle as well.


Edit 2: Found a neat trick that makes it easy to do this with setTimeout as well.

function imScrolling(scrollTxt, delay, index, target) {
    while (index < scrollTxt.length) {
        setTimeout(appendLetter, index * delay, scrollTxt, index);
        index++;
    }
}
function appendLetter(scrollTxt, index) {
    $('#myTxt').append(scrollTxt[index]);
}

See it working in this fiddle. The trick is multiplying the delay by index (so first timeout is set at 0*500, second at 1*500, third at 2*500 etc). Hope this also demonstrates the passing of parameters.

Upvotes: 0

Roko C. Buljan
Roko C. Buljan

Reputation: 206151

> Live demo

function imScrolling(txt, intv, i, elID){

  var $el = $(elID),
      tot = txt.length,
      interv = setInterval(doIt, intv);   

  function doIt(){
    $el.append(txt[i++]);
    if(i==tot) clearInterval(interv);
  }
  
}

Now something interesting:

How to do it using the jQuery's animation method:

Instead of setInterval, if you want to control the total time that occurs to do the animation you can use
.animate() and it's step function:

> Live demo (3500ms)

function imScrolling(txt, time, i, elID){
  
    $({c:i}).animate(
      {c:txt.length},{
       duration : time,
       easing : 'linear',
       step : function(now){
           d = ~~(now);
           r = ~~(this.c);
           if(d!==r)$(elID).append(txt[r]);
       }
      }
    );     
  
}

Upvotes: 0

dajavax
dajavax

Reputation: 3400

The problem is setTimeout is asynchronous but your function keeps running the while synchronously and so setTimeout is being set many times immediately. This is what I suggest:

function imScrolling(scrollTxt, interval, index, target)
{
   setTimeout(function (){
       $('#myTxt').append(scrollTxt[index]);
       if(index < scrollTxt.length){
        imScrolling(scrollTxt, interval, index+1, target);
       }
       }, interval);
}

This way, you will set the interval again after the first one fires and not "at the same time". Demo

Upvotes: 1

Tibos
Tibos

Reputation: 27823

First of all let's start wth the function you call with setTimeout.

function (scrollTxt, interval, index, target) {
  $('#myTxt').append(scrollTxt[index]);
}

You have defined a function with 4 parameters that gets called at a later time. The problem is that the function given to setTimeout isn't passed any parameters when it's called, so inside the anonymous function scrollTxt, interval, index and target will be undefined.

To get over this issue you can use a closure. Inside the anonymous function you can use variables available in the scope the anonymous function is defined:

 var a = 5;
 function () {
    console.log(a); // 5
 }

There is one more issue. The variables used are the actual variables, not copies. So if you change the variable outside, the value inside the anonymous function will change as well:

var a = 5;
var f = function () {
  console.log(a);
}
a = 10;
f(); // outputs 10

The variable that changes for you is index. To get over that, you need to create a copy of it for each iteration. What better way to do that than to give it as parameter to a function?

for (var i=0; i<10; i++) {
  setTimeout((function(new_i) {
     return function () {
       console.log(new_i);
     };
  })(i),100);
}
// outputs 0,1,2,3,4,5,6,7,8,9

Apply this and you can easily solve your problem (and understand how you're solving it).

Upvotes: 0

Willian Andrade
Willian Andrade

Reputation: 332

Try not use event attributes on HTML.

jQuery helps you with it.

Try:

HTML

<div id="container">
    <p id="myTxt"></p>
    <input type="button" value="Submit" id="your-button" /> 
</div>

JAVASCRIPT

$(function () // jquery dom ready
{
    $("#container")
        .on("click", "#your-button", function ()
        {
            var yourText = "Hello World!!".split('');

            for (var i = 0; i < yourText.length; i++)
            {
                $("#myTxt")
                    .append(yourText[i])
                    .append("<br/>"); // just to line break
            }
        }
});

Working fiddle

Upvotes: 0

Related Questions