For loop for HTMLCollection elements
In response to the original question, you are using for/in
incorrectly. In your code, key
is the index. So, to get the value from the pseudo-array, you'd have to do list[key]
and to get the id, you'd do list[key].id
. But, you should not be doing this with for/in
in the first place.
Summary (added in Dec 2018)
Do not ever use for/in
to iterate a nodeList or an HTMLCollection. The reasons to avoid it are described below.
All recent versions of modern browsers (Safari, Firefox, Chrome, Edge) all support for/of
iteration on DOM lists such nodeList
or HTMLCollection
.
Here's an example:
var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}
To include older browsers (including things like IE), this will work everywhere:
var list = document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
console.log(list[i].id); //second console output
}
Explanation For Why You Should Not Use for/in
for/in
is meant for iterating the properties of an object. That means it will return all iterable properties of an object. While it may appear to work for an array (returning array elements or pseudo-array elements), it can also return other properties of the object that are not what you are expecting from the array-like elements. And, guess what, an HTMLCollection
or nodeList
object can both have other properties that will be returned with a for/in
iteration. I just tried this in Chrome and iterating it the way you were iterating it will retrieve the items in the list (indexes 0, 1, 2, etc...), but also will retrieve the length
and item
properties. The for/in
iteration simply won't work for an HTMLCollection.
See http://jsfiddle.net/jfriend00/FzZ2H/ for why you can't iterate an HTMLCollection with for/in
.
In Firefox, your for/in
iteration would return these items (all the iterable properties of the object):
0
1
2
item
namedItem
@@iterator
length
Hopefully, now you can see why you want to use for (var i = 0; i < list.length; i++)
instead so you just get 0
, 1
and 2
in your iteration.
Evolution of Browser Support for NodeList and HTMLCollection iteration
Following below is an evolution of how browsers have evolved through the time period 2015-2018 giving you additional ways to iterate. None of these are now needed in modern browsers since you can use the options described above.
Update for ES6 in 2015
Added to ES6 is Array.from()
that will convert an array-like structure to an actual array. That allows one to enumerate a list directly like this:
"use strict";
Array.from(document.getElementsByClassName("events")).forEach(function(item) {
console.log(item.id);
});
Working demo (in Firefox, Chrome, and Edge as of April 2016): https://jsfiddle.net/jfriend00/8ar4xn2s/
Update for ES6 in 2016
You can now use the ES6 for/of construct with a NodeList
and an HTMLCollection
by just adding this to your code:
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Then, you can do:
var list = document.getElementsByClassName("events");
for (var item of list) {
console.log(item.id);
}
This works in the current version of Chrome, Firefox, and Edge. This works because it attaches the Array iterator to both the NodeList and HTMLCollection prototypes so that when for/of iterates them, it uses the Array iterator to iterate them.
Working demo: http://jsfiddle.net/jfriend00/joy06u4e/.
Second Update for ES6 in Dec 2016
As of Dec 2016, Symbol.iterator
support has been built-in to Chrome v54 and Firefox v50 so the code below works by itself. It is not yet built-in for Edge.
var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}
Working demo (in Chrome and Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/
Third Update for ES6 in Dec 2017
As of Dec. 2017, this capability works in Edge 41.16299.15.0 for a nodeList
as in document.querySelectorAll()
, but not an HTMLCollection
as in document.getElementsByClassName()
so you have to manually assign the iterator to use it in Edge for an HTMLCollection
. It is a total mystery why they'd fix one collection type, but not the other. But, you can at least use the result of document.querySelectorAll()
with ES6 for/of
syntax in current versions of Edge now.
I've also updated the above jsFiddle so it tests both HTMLCollection
and nodeList
separately and captures the output in the jsFiddle itself.
Fourth Update for ES6 in Mar 2018
Per mesqueeeb, Symbol.iterator
support has been built-in to Safari too, so you can use for (let item of list)
for either document.getElementsByClassName()
or document.querySelectorAll()
.
Fifth Update for ES6 in Apr 2018
Apparently, support for iterating an HTMLCollection
with for/of
will be coming to Edge 18 in Fall 2018.
Sixth Update for ES6 in Nov 2018
I can confirm that with Microsoft Edge v18 (that is included in the Fall 2018 Windows Update), you can now iterate both an HTMLCollection and a NodeList with for/of in Edge.
So, now all modern browsers contain native support for for/of
iteration of both the HTMLCollection and NodeList objects.
Unable to loop through HTMLCollection with forEach
HTMLCollection is an array-like object but it does not have forEach
method.
There are multiple ways you can iterate it with;
e.g.
Create an array out of it;
Array.from(toggleButton).forEach((el) =>
el.addEventListener("click", (event) => {
const card = event.target.parentElement.querySelector(".director__card");
card.classList.toggle("open");
})
);
Use for..of
for (let item of toggleButton) {
item.forEach((el) =>
el.addEventListener("click", (event) => {
const card = event.target.parentElement.querySelector(".director__card");
card.classList.toggle("open");
});
);
}
Or, just for
loop.
for (let i = 0, len = toggleButton.length; i < len; i++) {
toggleButton[i].forEach((el) =>
el.addEventListener("click", (event) => {
const card = event.target.parentElement.querySelector(".director__card");
card.classList.toggle("open");
});
);
}
typescript for loop over HTMLCollection
The typescript compiler supports this after specifying an extra flag, downlevelIteration
:
{
"compilerOptions": {
"target": "es5",
"downlevelIteration": true
}
}
However, this will not only remove the error, it will also change the compiler output.
This input typescript:
function compileTest(){
let elements = document.getElementsByTagName("p");
for(let element of elements){
console.log(element);
}
}
is compiled to this javascript:
function compileTest() {
var e_1, _a;
var elements = document.getElementsByTagName("p");
try {
for (var elements_1 = __values(elements), elements_1_1 = elements_1.next(); !elements_1_1.done; elements_1_1 = elements_1.next()) {
var element = elements_1_1.value;
console.log(element);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (elements_1_1 && !elements_1_1.done && (_a = elements_1.return)) _a.call(elements_1);
}
finally { if (e_1) throw e_1.error; }
}
}
How to loop through htmlcollection object?
It's not really clear, but are you looking for something like this?
targets = document.querySelectorAll('#tivvits > .grid-container')
for (let target of targets)
{console.log(target.id)}
This should select all <div>
nodes which are direct children of the <div id="tivvits">
node and have a class
attribute with the value "grid-container"
, and extract from them the attribute value of the id
attribute.
How to iterate through HTMLCollection in Javascript
this works for me
var links = document.getElementsByTagName('a');
var result = [].slice.call(links)
Using entries() in a for-of loop, iterating over an HTMLCollection
Entries method is available for arrays. However, getElementsByClassName
does not return an array. Instead it returns an HTMLCollection
. You need to first convert it into array. There are two ways you can do that:
- Using
Array.prototype.slice
function toArray(arr) {
return Array.prototype.slice.call(arr);
}
toArray(document.getElementsByClassName('foo'));
- Using ES6 spread
function toArray(arr) {
return [...arr];
}
toArray(document.getElementsByClassName('foo'));
How to check if all elements in an HTMLCollection contain a certain value for a property?
You can use spread syntax to convert the NodeList/HTMLCollection to an array, then use Array.every
to check whether each item in the array matches the condition in the callback:
const res = [...document.querySelectorAll('input')].every(e => e.checked)
console.log('All checkboxes checked?', res)
<input type="checkbox" checked>
<input type="checkbox" checked>
<input type="checkbox" checked>
Why and when for loop ignore some item with html collection
The problem here is that you are using document.getElementsByClassName
which gives you a live NodeList. You should use document.querySelectorAll('.test')
instead.
var newtest = document.querySelectorAll('.test');
for (var i=0; i < newtest.length; i++) {
newtest[i].className = "bob";
}
With live NodeList collection newtest
is a reference to a dynamically updated collection of elements (indexed by className test
). So after the first loop iteration when you overwrite className with bob
the whole collection becomes smaller by one (because newtest[0]
no longer has class test
). It makes indices shift.
One more problem with your code: don't forget var
keywords.
Demo: http://codepen.io/anon/pen/BAFyb
Related Topics
Find an Element in Dom Based on an Attribute Value
Response to Preflight Request Doesn't Pass Access Control Check
Whats the Best Way to Update an Object in an Array in Reactjs
Clientheight/Clientwidth Returning Different Values on Different Browsers
Count Animation from Number a to B
Using Jquery to Know When @Font-Face Fonts Are Loaded
CSS Animation Doesn't Restart When Resetting Class
How to Group Data with an Angular Filter
Why Does ++[[]][+[]]+[+[]] Return the String "10"
How to Avoid 'Cannot Read Property of Undefined' Errors
Best Way to Detect When a User Leaves a Web Page
Difference Between Two Dates in Years, Months, Days in JavaScript
Am I Using Too Much Jquery? When am I Crossing the Line
How to Make Changeable Themes Using CSS and JavaScript
Access CSS File Contents via JavaScript