16

I have an issue where I have recursion inside of a for loop:

function func(node) {
    for(var i = 0; i < node.children.length; i++) {
       func(node.children[i]);
    } 
} 

Obviously because JavaScript does not have block scope, the same i variable is getting modified each time the function is called. What is the best way to remedy this? Assume regular EcmaScript 3 and I can't use JavaScript 1.7 "let".

I know this has been asked before, but the other questions don't seem to show recursion, they show one function call where a closure could be used.

mcot
  • 1,309
  • 3
  • 16
  • 19
  • 2
    Can you post some example data for `node`? JavaScript scopes variables to the containing `function` or object literal, so each recursive call to `func` should get its own `i`. – Stoive Jun 21 '11 at 00:41
  • Agree. tested in some other case. Can you post some example in jsfiddle.com ? – Liangliang Zheng Jun 21 '11 at 00:44
  • 1
    This function looks like it just loops (recursively) for a bit (assuming that the node has children)...is there something that it's supposed to do? – Gerrat Jun 21 '11 at 00:45
  • If you don't want text nodes, then your code should work fine (in browsers that support `children`). Just throw a little code in there that does something to `node`, and you're set. Paste this code into the console, and you'll get a nice orange border on everything: `function func(node) {for(var i = 0; i < node.children.length; i++) {if(node.nodeType === 1) node.style.border="1px solid orange";func(node.children[i]);}} func(document.body);` – user113716 Jun 21 '11 at 00:59
  • ok, this was an unrelated problem where the length of children was being modified. Sorry for the confusion. – mcot Jun 21 '11 at 02:19

7 Answers7

17

Cache the length of the array so you would have the following:

function recurse(node) {
    for(var i = 0, count = node.children.length; i < count; i++) {
        recurse(node.children[i]);
    }
} 

You should always cache especially when you're dealing with HTMLCollections.

Michael Elliott
  • 307
  • 2
  • 15
MackPro
  • 198
  • 4
10

Just use Crockford's walkTheDOM function:

function walkTheDOM(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walkTheDOM(node, func);
        node = node.nextSibling;
    }
}

You pass in the root node and the function that you want to run for each node, like so:

var root = document.getElementById('wrap');

walkTheDOM(root, function(node) {
    console.log( node.nodeName );
});

Live demo: http://jsfiddle.net/VKWTt/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
7

Was this facing issue, like during recursion of a function the variable values got replaced. the recursion was inside for loop, so the variables inside the for loop where modified.

Use var to declare variables that are modified at recursion.

karthick-sk
  • 81
  • 1
  • 2
  • 2
    refer link **[var and not using var](http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript)**, will give more insight on this. – karthick-sk Nov 16 '12 at 09:31
2

I think you example should work. The variable i is declared local, so when you recurse a new 'i' is used.

Javascript does global and local variables!

Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
2

You've already defined 'i' as a variable in a broader scope ;)

Daniel Teichman
  • 590
  • 3
  • 10
1

I'm a little confused. i is declared locally and so it's not the same i variable getting modified. Tested on this very page:

var span = document.getElementsByTagName("span")[0];
function func(node) {
    for(var i = 0; i < node.children.length; i++) {
       console.log([i, node]);
       func(node.children[i]);
    } 
}
func(span);

// returns
// [0, <span id="hlinks-user">...</span>]
// [1, <span id="hlinks-user">...</span>]
// [2, <span id="hlinks-user">...</span>]
// [0, <a href="/users...">...</a>]
// [3, <span id="hlinks-user">...</span>]
// [0, <span title="1 silver...">...</span>]
// [1, <span title="1 silver...">...</span>]
// [4, <span id="hlinks-user">...</span>]
// [0, <span title="7 bronze...">...</span>]
// [1, <span title="7 bronze...">...</span>]
// [5, <span id="hlinks-user">...</span>]
brymck
  • 7,555
  • 28
  • 31
0

This worked for me.

 function DragDropChanges(nodeChanged) {
        if (nodeChanged.children != null) {
            for (i = 0; i < nodeChanged.children.length;
                var temp = i;
                DragDropChanges(nodeChanged.children[i]);
                i = temp;
            }
        }
    }
kchr
  • 1
  • 3