9

I have some elements in my HTML with class node-item, I access them in my component using:

let nodeItems = document.getElementsByClassName('node-item');

and when I log nodeItems it gives me a HTMLCollection[] with length 4.

I tried many ways but still can't iterate on nodeItems:

1- first try:

let bar = [].slice.call(nodeItems);
for (var g of bar){
    console.log(g); //gives me nothing
} 

2- second try:

for(let c of <any>nodeItems) {
    console.log(c); //gives me nothing
}

And I tried array iteration and object iteration but still undefined or error. also tried:

let nodeItems = document.querySelector(selectors);

But same problems.

HDJEMAI
  • 7,766
  • 41
  • 60
  • 81
Fateme Fazli
  • 9,858
  • 2
  • 26
  • 42
  • 1
    Both approaches should work (though `Array.from()` would be cleaner than `[].slice.call()`). Are you sure the collection does contain the elements? Remember that it is a *live* node list. Please do `console.log(nodeItems.length)`. – Bergi Apr 21 '18 at 13:26
  • 2
    Your code is working when doing it in the console, do you have more example? – romuleald Apr 21 '18 at 13:26
  • 1
    @Bergi @romuleald i log the ```nodeItems``` and it's give me a full HTMLCollection but when log the length it gives me 0 . – Fateme Fazli Apr 21 '18 at 13:33
  • 2
    Then your actual problem is simply that the collection is empty when you try to iterate it, and it only gets filled later (when the DOM elements have been loaded). – Bergi Apr 21 '18 at 13:36
  • @Bergi This is not a duplicate of listed questions because the question is specific to TS. I'm quite sure there's a canonical TS question somewhere though. – Estus Flask Apr 21 '18 at 13:39
  • @estus What does typescript have to do with this? Regardless whether `for of` compiles to iterators or array-index-counting, it should work. If logging the length gives `0`, as per the OPs comment, then the problem is with using the DOM at the wrong time. Could you please re-close it? – Bergi Apr 21 '18 at 13:42
  • @Bergi Yes, length 0 is actual problem here. It will work with `for..of` because it compiles to `for` but there will be compilation error (nodeItems is ugly workaround for that). Can you list the dupes you've marked? I didn't expect it to be instantly reopened. – Estus Flask Apr 21 '18 at 13:48
  • @estus I had https://stackoverflow.com/questions/14028959/why-does-jquery-or-a-dom-method-such-as-getelementbyid-not-find-the-element, and also [the canonical question for `console.log` confusion on mutated arrays](https://stackoverflow.com/q/4057440/1048572) – Bergi Apr 21 '18 at 14:12

3 Answers3

19

nodeItems is HTMLCollection, which is array-like object.

It is iterable in modern browsers. Iterators are supported with downlevelIteration compiler option enabled, in this case it will be:

const nodeItems = document.getElementsByClassName('node-item');

for (const c of nodeItems) {
  // ...
}

Iterables can be polyfilled in older browsers. core-js provides polyfills for DOM iterables.

Otherwise nodeItems can be converted to array and iterated as usual:

const nodeItems = Array.from(document.getElementsByClassName('node-item'));

for (const c of nodeItems) {
  // ...
}
Estus Flask
  • 150,909
  • 47
  • 291
  • 441
7

Just use Array.from(document.getElementsByClassName('node-item')) or the spread operator [...document.getElementsByClassName('node-item')] and use whatever you would use on an array.

Apart from that, you could also use a normal for loop

let nodeItems = document.getElementsByClassName('node-item');
for (let i = 0; i < nodeItems.length; i++) {
    // access current element with nodeItems[i]
}
baao
  • 62,535
  • 14
  • 113
  • 168
  • i try the code but console logs nothing. my HTMLCollection is full and i log it and when tried ```[...document.getElementsByClassName('node-item')]``` it gives me error ```HTMLCollectionOf' is not an array type```. – Fateme Fazli Apr 21 '18 at 13:35
3

You can use spread operator on document.querySelectorAll to have an array.

Here is a snippet:

let nodeItems = [...(document.querySelectorAll('.class1'))];

for (var g of nodeItems) {
  console.log( g.innerHTML ); 
}
<div class='class1'>Text 1</div>
<div class='class1'>Text 2</div>
<div class='class1'>Text 3</div>

Doc: Spread

Eddie
  • 25,279
  • 6
  • 26
  • 53