Infinite loop when walking recursively through DOM nodes

Asked

Viewed 245 times

7

I have a div with contenteditable=true in my document and I intend to do Highlight in certain parts of the text while the user type (add spans). For this I first need to extract the text typed by the user in the form of a simple string, without tags, and preserving line breaks. Thus <p>aa</p><p>bb</p> would generate "aa\nbb" and <div>aaa</div><div><br></div> would generate "aaa\n". For this I created a function that is called in the oninput of the div and that should return an array with each line of text (["aa", "bb"] and ["aaa", ""] for the above examples).

Initially I did focusing only on Chrome. It creates the content of the div as the user types producing internal Ivs for each line. In some cases Divs can be generated inside each other. Ex: (identation has been added for readability)

<div contenteditable=true>
  primeira linha
  <div>segunda linha</div>
  <div><br></div>
  <div>
    quarta linha
    <div>quinta linha</div>
  </div>
</div>

I made the following code to get the resulting array:

function extractLines(elem) {
    var nodes = elem.childNodes;
    var lines = [];
    for (i = 0; i < nodes.length; ++i) {
        var node = nodes[i];
        if (node.nodeName == "#text") {
            lines.push(node.nodeValue);
        }
        else if (node.nodeName == "BR") {
            lines.push("");
        }
        else { // DIV ou P
            lines.push.apply(lines, extractLines(node));
        }
    }
    return lines;
}

The idea is quite simple:

  • For each subnode of the div:

    1. If it is a text node, it is a line. Include in the array.
    2. If it is a <br>, is a blank line. Include "" in the array.
    3. If it is a <div> or <p>, recursively execute this algorithm and insert the result at the end of the array.

But this same code generates an endless loop in some of my tests, I don’t understand why.

  • <div>aa<div><br></div></div> => ["aa", ""]
  • <div>aa<div><br></div><div><br></div></div> => infinite loop

Why this infinite loop happens? Is there something wrong with my code? How to implement this same algorithm without this problem?

2 answers

7

You forgot to declare the variable i using var, which made her a global. The recursive call overrides its value, so that when it returns it iterates again over existing elements (entering the recursion again and coming back again).

Add the var and the function will be correct:

for (var i = 0; i < nodes.length; ++i) {
  • +1 for a brilliant solution... ;)

6


I spent a good few minutes debugging the code on a fiddle, until I realized a very, very subtle nuance.

The control variable of your loop for is declared as a global variable without the var, soon she’s getting "dirt" on every recursion.

Change the code as follows:

...
for (var i = 0; i < nodes.length; ++i) {
...
  • 1

    haha me "hit" for 13 seconds... P

  • 2

    I’ll have to take it for 13 seconds. It worked!

  • That was the most curious situation I’ve ever seen... :)

  • I was also looking and missed the var, was perplexed.

  • 1

    @bfavaretto Perplexed².

  • 1

    I was perplexed! = D

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.