Sriram
Sriram

Reputation: 10558

Timeout function to highlight words does not work correctly

I am using the setTimeout and clearTimeout functions to highlight words in a textarea element at certain specified intervals. The steps I am following are:

  1. Get text from textarea element.
  2. Split into words.
  3. Output each word into a separate textarea element every 5 seconds.

The code is:

<html>
<head>
<script type="text/javascript">

    /* this function does the folloeing:
     * 1. takes all the words in the textarea.
     * 2. Separates them into words.
     * 3. Iterate through the list of words.
     * 4. Search and highlight each word for 1 second.
     */

    var highlightBtn = document.getElementById("start");
    var continueHighlight = false;
    var text;
    var highlighter;
    var words;

    function startHighlight(val) {

        continueHighlight = val;
        console.log("Highlight = " + continueHighlight);

        //1. get words within textarea.
        var textarea = document.getElementById("inputText");
        text = textarea.value;
        console.log("text = " + text);

        //2. split text into words.
        var words = text.split(' ');
        console.log("There are " + words.length + " words in the text.");

        //highlight();
        if(continueHighlight) {
            //3. iterate through list of words.
            var i = 0;
            while(i < words.length) {
                highlighter = setTimeout(searchAndHighlight, 5000, words[i]);
                //console.log("Word highlighting = " + words[i]);
                i = i + 1;
            }
        } else {
            console.log("Stopping highlighting.");
            clearTimeout(highlighter);
        }
    }

    function highlight() {
        if(continueHighlight) {
            //3. iterate through list of words.
            var i = 0;
            while(i < words.length) {
                highlighter = setTimeout(searchAndHighlight, 5000, words[i]);
                //console.log("Word highlighting = " + words[i]);
                i = i + 1;
            }
        } else {
            console.log("Stopping highlighting.");
            clearTimeout(highlighter);
        }
    }

    function searchAndHighlight(word) {
        console.log("Highlighting word = " + word);
        var output = document.getElementById("output");
        output.value = word;
    }

</script>
</head>

    <body>
        <textarea id="inputText"></textarea>
        <br/>
        <button id="start" onclick="startHighlight('true')">Start!</button>
        <br/>
        <button id="stop" onclick="startHighlight('false')">Stop!</button>
        <br/>
        <textarea id="output"></textarea>

    </body>

</html>

I expected every word to be displayed in the second textarea every 5 seconds. This is not happening. Instead, I get nothing for 5 seconds and then all words in quick succession. This happens even if I press the stop button. My questions:

Where am I going wrong in using the setTimeout function?

Upvotes: 0

Views: 385

Answers (2)

Robert Munn
Robert Munn

Reputation: 798

Here is a solution that fixes the stop button problem. If you keep track of i, you could also have the highlight function resume where it left off.

<html>
<head>
<script type="text/javascript">

/* this function does the folloeing:
 * 1. takes all the words in the textarea.
 * 2. Separates them into words.
 * 3. Iterate through the list of words.
 * 4. Search and highlight each word for 1 second.
 */

var highlightBtn = document.getElementById("start");
var continueHighlight = false;
var text;
var highlighter;
var words;

function startHighlight(val) {

    continueHighlight = val;
    console.log("Highlight = " + continueHighlight);

    //1. get words within textarea.
    var textarea = document.getElementById("inputText");
    text = textarea.value;
    console.log("text = " + text);

    //2. split text into words.
    var words = text.split(' ');
    console.log("There are " + words.length + " words in the text.");

    //highlight();
    //3. iterate through list of words.
    var i = 0;

     highlighter = setTimeout( function( ){
                searchAndHighlight( words, 0 );
            }, 5000);

    function searchAndHighlight(words, i ) {
        if(i >= words.length){
            clearTimeout(highlighter);
        }else{
            console.log("Highlighting word = " + words[i]);
            var output = document.getElementById("output");
            output.value = words[i];
            i++;
             highlighter = setTimeout( function( ){
                    searchAndHighlight( words, i );
                }, 5000);
        }
    }
}

function stopHighlight(){
  console.log("Stopping highlighting.");
  clearTimeout(highlighter);
}


</script>
</head>

    <body>
        <textarea id="inputText"></textarea>
        <br/>
        <button id="start" onclick="startHighlight()">Start!</button>
        <br/>
        <button id="stop" onclick="stopHighlight()">Stop!</button>
        <br/>
        <textarea id="output"></textarea>

    </body>

</html>

Upvotes: 1

jfriend00
jfriend00

Reputation: 707318

This piece of code:

        var i = 0;
        while(i < words.length) {
            highlighter = setTimeout(searchAndHighlight, 5000, words[i]);
            //console.log("Word highlighting = " + words[i]);
            i = i + 1;
        }

is rapidly calling setTimeout() a bunch of times in a row and setting them all for the same time. That will match your symptoms of waiting 5 seconds and then running them all at once.

You will either have to set each successive timeout for a longer period of time or change the structure of the code to not start the next timer until the first one fires.


Here's how you could solve it by only setting one setTimeout() at a time. This would also solve the stop button problem:

function highlight() {
    if(continueHighlight) {
        var i = 0;

        function next() {
            if (i < words.length) {
                highlighter = setTimeout(function() {
                    searchAndHighlight(words[i]);
                    i++;
                    next();
                }, 5000);
            }
        }

        next();
    } else {
        console.log("Stopping highlighting.");
        clearTimeout(highlighter);
    }
}

When you want to stop the progressive highlighting, I don't understand why you're taking such a circuitous route. You should just call the clearTimeout(highlighter). There's no need to go through multiple functions like you're doing just to do that.

Or, the solution that sets successively longer timers could work like this:

        var i = 0;
        while(i < words.length) {
            highlighter = setTimeout(searchAndHighlight, 5000 * (i+1), words[i]);
            //console.log("Word highlighting = " + words[i]);
            i = i + 1;
        }

If you want to use this type of solution, then to fix the stop button problem, you will have to keep all the timer ids in an array and cancel all of them. Personally, I'd probably restructure the code and only have one setTimeout() in flight at a time and when each one fires you kick off the next one. Then, you only have one setTimeout() going at once so it's simpler to cancel.

Upvotes: 1

Related Questions