Strange result using "for(in... " iteration

Joined
Jan 6, 2022
Messages
24
Reaction score
0
In playing around with iteration methods I stumbled across this confusing outcome.

Method 1 code with a standard loop has the expected array property length of 2 and alerts for each pet but the second method using a "for(in …” iteration gives an extra 3 alerts . So it seems “arr” has extra (undefined) items. Hope someone can tell me why.

And, yes, I’m using JS to write the HTML elements instead of hard coding them.

Code:
<script>
    function go(){
arr=document.getElementsByClassName("pet");
        
        alert("method 1")
           for(i=0;i<arr.length;i++) // 2 alerts
        {alert(arr[i].innerHTML)} // show contents
                
        alert('method 2');
        for ((n) in arr)          // 5 alerts
        {alert(arr[n].innerHTML)} // show contents

}
    document.write
    ("<div class='pet' hidden>dog</div>\
    <div class='pet' hidden>cat</div>\
<button onclick=go()>click to run</button>")
</script>
 
Joined
Sep 4, 2022
Messages
158
Reaction score
16
hello !



by the 'for (n) in arr' , you are reading array properties and its own content.
it returns your array custom settings, and all vars in. it makes more datas to fetch.

to avoid weird feedbacks stay on 'for loop'
 
Joined
Jul 4, 2023
Messages
589
Reaction score
78
The arr returned by document.getElementsByClassName('pet') is not a regular JavaScript array, but an HTMLCollection. While it behaves like an array in some respects, it is technically an object with enumerable properties. In this case:
  • The HTMLCollection might have additional properties that are not part of the actual "pet" elements, which are being iterated over with the for-in loop.
  • When you use for-in, it loops over every property in the object, including non-numeric keys (like: length, item, namedItem, etc.), which results in extra alerts for undefined or irrelevant items.
Use the test without innerHTML and let's see what happens [ on-line ].
HTML:
<div class='pet' hidden>dog</div>
<div class='pet' hidden>cat</div>
<button onclick=test()>click to run test</button>
<pre></pre>

<script>
  const console_ = document.querySelector('pre');
 
  function test() {
    const html_collection = document.getElementsByClassName('pet');
    console_.textContent += [...html_collection].map(e => e.outerHTML).join('\n');
    showMethodOne('getElementsByClassName', html_collection);
    showMethodTwo('getElementsByClassName', html_collection);
 
    console_.textContent += '\n\n';
 
    const node_list = document.querySelectorAll('.pet');
    console_.textContent += [...node_list].map(e => e.outerHTML).join('\n');
    showMethodOne('querySelectorAll', node_list);
    showMethodTwo('querySelectorAll', node_list);
  }
 
  function showMethodOne(name, object) {
    console_.textContent += '\n\n' + `Method 1 - ${name}` + '\n';
    for (let i=0; i<object.length; i++)
      console_.textContent += `${object[i]} ${object[i].nodeName} `
        + `${object[i].className} `
        + `${object[i].textContent} `
        + `[${[...object[i].attributes].map(a => a.name).join(', ')}]`
        + '\n';
  }
  function showMethodTwo(name, object) {
    console_.textContent += '\n\n' + `Method 2 - ${name}` + '\n';
    for (const n in object)
      if (! Number(object[n]))
        console_.textContent += object[n] + '\n';
      else
        console_.textContent += 'properties length ' + object.length + ' [read only]' + '\n';
  }
</script>

<style>
  pre {
    width: 90dvw;
    background-color: black;
    color: limegreen;
    padding: .5rem;
  }
  pre:empty {
    opacity: 0;
  }
</style>
1732607035955.png


BTW,
HTML:
<div class='pet' hidden>dog</div>
<div class='pet' hidden>cat</div>
<button onclick=test()>click to run test</button>
<pre></pre>

<script>
  const console_ = document.querySelector('pre');

  function test() {   
    const html_collection = document.getElementsByClassName('pet');
    showTitle('getElementsByClassName');
    console_.textContent += [...html_collection].map(e => e.outerHTML + ' ' + e).join('\n') + '\n';

    showTitle('Method - for...of');
    for (const n of html_collection)
      console_.textContent += `${n} ${n.nodeName} ${n.innerText}` + '\n';
    showTitle('Method - forEach');
    [...html_collection].forEach((n) => {
      console_.textContent += `${n} ${n.nodeName} ${n.innerText}` + '\n';
    });

    console_.textContent += '\n';

    const node_list = document.querySelectorAll('.pet');
    showTitle('querySelectorAll');
    console_.textContent += [...node_list].map(e => e.outerHTML + ' ' + e).join('\n')+ '\n';

    showTitle('Method - for...loop');
    for (let i=0; i<node_list.length; i++)
      console_.textContent += `${node_list[i]} ${node_list[i].nodeName} ` + '\n';
    showTitle('Method - for...of');
    for (const n of node_list)
      console_.textContent += `${n} ${n.nodeName} ${n.innerText}` + '\n';
    showTitle('Method - forEach');
    node_list.forEach((n) => {
      console_.textContent += `${n} ${n.nodeName} ${n.innerText}` + '\n';
    });
  }

  function showTitle(name) {
    console_.textContent += '\n' + name + '\n';
  }
</script>

<style>
  pre {
    width: 90dvw;
    background-color: black;
    color: limegreen;
    padding: .5rem;
  }
  pre:empty {
    opacity: 0;
  }
</style>
1732611162007.png
 
Last edited:

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,260
Messages
2,571,039
Members
48,768
Latest member
first4landlord

Latest Threads

Top