Hal_9100 Hal_9100 - 1 month ago 6
Javascript Question

function calling itself with setInterval (browser freezing issue)

I know there already are similar questions to mine but none of the answers I read solved my problem.

I wrote a small script that prints a string letter by letter using two functions : first one spells the string, second one calls the first using

setInterval
as long as the word isn't entirely spelled :

function write() {
writeThis = str.substring(0, currentLetter);
div.innerHTML = writeThis;
currentLetter++;
console.log('running');
}

function start() {
setInterval(function() {
if (currentLetter > str.length) {
return false;
}
write(); //calls the writing function
}, 100);
}
start();


I need to merge these two functions into one in order to dynamically use it on different strings by passing the strings as a parameter. I'm trying to do something like this :

function write(thisString) {
//code
return thisString;
}
var str1 = "hello world";
var str2 = "foo bar";
write(str1); write(str2);


I'm stuck when it comes to insert
setInterval
in
write()
function to call itself. No matter what I try
write()
will just call itself forever (despite the
if
condition) and freeze the browser. Is it possible to merge these two functions into one ?

Demo here : https://jsfiddle.net/Hal_9100/b5gm8nbc/

Answer

setInterval will keep firing until you call clearInterval(intervalId).

For most animation, it's easier to control with setTimeout in any case, as follows:

var div = document.getElementById('div');
var str = 'hello world.';
var currentLetter = 1;

var writeThis = str.substring(0, currentLetter);

function write() {
    writeThis = str.substring(0, currentLetter);
    div.innerHTML = writeThis;
    currentLetter++;
    console.log('running'); 
}

function run(){
    if (currentLetter > str.length) {
        return false;
    }
    write();
    setTimeout(run, 100);
}

function start() {
  setTimeout(run, 100);
}
start();

If you really want to use setInterval, the function returns the intervalId you need, so you'd do something like:

var intervalId = setInterval(fn, 100);

then in the if statement, you'd call

clearInterval(intervalId);

Obviously, you need to make sure intervalId is in scope in both places.

** EDIT **

Adding making it one function. Sorry I missed that part of the question. The trick here is to use Function.bind()

var div = document.getElementById('div');
var str = 'hello world.';
var currentLetter = 1;

var writeThis = str.substring(0, currentLetter);

function run(text){
    if (currentLetter > text.length) {
        return false;
    }
    writeThis = text.substring(0, currentLetter);
    div.innerHTML = writeThis;
    currentLetter++;
    console.log('running');
    setTimeout(run.bind(null, text), 100);
}

run(str);

If you wanted to pass in everything (e.g. the div, the str, the current letter), just add them as params to the run function and add them when you bind again.