103

I have this code in a function:

tableFields = tableFields.children;
for (item in tableFields) {
    // Do stuff
}

According to a console.log of tableFields, I am getting an array back as I assume I need to do. A console.log of item within the loops returns undefined. What do I have to do to loop through tableFields and insert each object into a table?

console log of tableFields:

HTMLCollection[label, input, label, input 25, label, input, input, input Remove]


0
label

1
input

2
label

3
input 25

4
label

5
input

6
input

7 
input Remove

description[]
input

hours[]
input

invoice_number
input

getlength
8

rate[]
input 25

item
item()

iterator
iterator()

namedItem
namedItem()

__proto__
HTMLCollectionPrototype { item=item(), namedItem=namedItem(), iterator=iterator()}

Here is the entire section of code as I have so far:

$this->title("Test");
    $this->defaultMenu();
    $select = "";
    $names = Customer::getNames();
    foreach ($names as $id => $name) {
        $select .= '<option value="'.$id.'"';
        if ($this->customerid == $id) $select .= ' selected ';
        $select .= '>'.$name.'</option>';
    }

    $form = '
<script type="text/javascript">

var counter = 0;

function isEven(int){
int = Number(int);
return (int%2 == 0);
}



function moreLabor() {

    var table = document.getElementById("editTable");
    var tableFields = document.getElementById("readroot");

    tableFields = tableFields.children;
    console.log(tableFields);
    for (item in tableFields) {

        if (isEven(counter)) {
            var tableRow = table.insertRow(-1);
            var label = tableRow.insertCell(-1);
            console.log(tableFields[item]);
            label.appendChild(tableFields[item]);

        } else {
            var field = tableRow.insertCell(-1);
            field.innerHTML = item.innerHTML;


        }

        counter++;
    }

    console.log();
var insertHere = document.getElementById("writeroot");
}

window.onload = function(){
    document.getElementById(\'moreLabor\').onclick = function(){ moreLabor(); }
    moreLabor();
}


</script>

<div id="readroot" style="display: none">
<tr>
    <td><label for="hours">Hours:</label></td>
    <td><input type="text" name="hours[]" value="" /></td>
</tr>
<tr>
    <td><label for="rate">Rate:</label></td>
    <td><input type="text" name="rate[]" value="25" /></td>
</tr>
<tr>
    <td><label for="description">Description:</label></td>
    <td><input type="text" name="description[]" value="" /></td>
</tr>

<input type="hidden" name="invoice_number" value="'.$this->number.'" />
<tr>
    <td><input type="button" value="Remove"
    onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /></td>
</tr>

</div>

<form method="POST" class="invoice" id="edit">
<table id="editTable">
    <tr>
        <td><label>Work Order Number:</label></td>
        <td><input type="text" name="number" value="'.$this->number.'"/></td>
    </tr>
    <tr>
        <td><label>Customer:</label></td>
        <td><select name="customerid">'.$select.'</select></td>
    </tr>
    <span id="writeroot"></span>

    <tr>
        <td><input type="button" id="moreLabor" value="Add labor"/></td>
        <td><input type="submit" name="Save" value="Save" /></td>
    </tr>';
    if (!is_null($this->id)) {
        $form .= '<input type="hidden" name="id" value="'.$this->id.'"/>';
    }
    $form .= '</table></form>';



    $this->component($form);

8 Answers 8

153

The trick is that the DOM Element.children attribute is not an array but an array-like collection which has length and can be indexed like an array, but it is not an array:

var children = tableFields.children;
for (var i = 0; i < children.length; i++) {
  var tableChild = children[i];
  // Do stuff
}

Incidentally, in general it is a better practice to iterate over an array using a basic for-loop instead of a for-in-loop.

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

8 Comments

so how do I get the inner elements of a parent element?
@CoreyRay: in my sample code "tableFields" is the parent element and "children" are the child elements.
I found this to work fine: for(let x of Array.from(tableFields.children)) {...}
This only looks at the direct children of tableFields; you want to use getElementsByTagName('*') to see all children and nested-children. See jsfiddle.net/Abeeee/gd3eokb5/5
@RocketNuts an HTMLCollection offers additional methods for handling html elements so it's more useful than an array. As noted above you can iterate through the items using for (ele of collection) ...
|
56

In ECS6, one may use Array.from() or Spread array syntax:

const listItems = document.querySelector('ul').children;
const listArray = Array.from(listItems);
// or
const listArray = [...listItems];
listArray.forEach((item) => {console.log(item)});

2 Comments

Strangely this method is much faster (about 15x in Chrome) than itering directly the children property like the first answer suggests. Wonder why...
How did you test that "15x" performance improvement? I would assume that Array.from() would perform better with tracing JIT compiler when the same code is executed thousands or millions of times in a loop. However, for a single loop, I would expect directly iterating through .children being faster.
16

Modern JS also uses the for..of to enable us to iterate DOM children objects, array, or other iterable objects. I think it is very clean and simple.

var children = tableFields.children;
for (c of children) { 
  console.log(c);
  // Do stuff with child c
}

1 Comment

Note add "dom.Iterable" to your tsconfig lib array. Otherwise TypeScript complains: Type 'HTMLCollection' must have a '[Symbol.iterator]()' method that returns an iterator.
7

if tableFields is an array , you can loop through elements as following :

for (item in tableFields); {
     console.log(tableFields[item]);
}

by the way i saw a logical error in you'r code.just remove ; from end of for loop

right here :

for (item in tableFields); { .

this will cause you'r loop to do just nothing.and the following line will be executed only once :

// Do stuff

1 Comment

Thanks, I made the suggested changes. I am trying to appendChild these items to a table row object
6

The backwards compatible version (IE9+) is

var parent = document.querySelector(selector);
Array.prototype.forEach.call(parent.children, function(child, index){
  // Do stuff
});

The es6 way is

const parent = document.querySelector(selector);
Array.from(parent.children).forEach((child, index) => {
  // Do stuff
});

Comments

5

In the year 2020 / 2021 it is even easier with Array.from to 'convert' from a array-like nodes to an actual array, and then using .map to loop through the resulting array. The code is as simple as the follows:

Array.from(tableFields.children).map((child)=>console.log(child))

2 Comments

Quick, time saving and the easiest one.
This needs to create an array, even two arrays. Better use the for...of construct as given in Agung's answer.
5

Using ES6,

[...element.children].map(child => console.log(child));

Comments

2

I’m surprised no-one answered with this code:

for(var child=elt.firstChild;
    child;
    child=child.nextSibling){
  do_thing(child);
}

Or, if you only want children which are elements, this code:

for(var child=elt.firstElementChild;
    child;
    child=child.nextElementSibling){
  do_thing(child);
}

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.