17

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.

5
  • 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. Commented Jun 21, 2011 at 0:41
  • Agree. tested in some other case. Can you post some example in jsfiddle.com ? Commented Jun 21, 2011 at 0: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? Commented Jun 21, 2011 at 0: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); Commented Jun 21, 2011 at 0:59
  • ok, this was an unrelated problem where the length of children was being modified. Sorry for the confusion. Commented Jun 21, 2011 at 2:19

7 Answers 7

18

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.

Sign up to request clarification or add additional context in comments.

Comments

11

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/

3 Comments

This works for DOM elements only but the question is asked in a general manner.
@FLekschas It works for all types of nodes: jsbin.com/daruzo/edit?html,js,console
Okay my wording was wrong. Nodes could anything, not just a DOM node, for example a plain object: jsbin.com/yepoqixowe/edit?html,js,console
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.

1 Comment

refer link var and not using var, will give more insight on this.
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!

Comments

2

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

Comments

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>]

Comments

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;
            }
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.