JavaScript iterate through NodeList [duplicate] - javascript

This question already has answers here:
Why doesn't nodelist have forEach?
(11 answers)
Looping through a nodelist JS
(1 answer)
Closed 3 years ago.
I am looking for a best way to iterate through NodeList excluding length. I am using:
var foo = document.querySelectorAll('[id^=foo_id]')
console.log(foo)
Returned NodeList has all the required elements + the last entry of length:.
0: input#foo_id_...
1: input#foo_id_...
..................
n: input#foo_id_...
length: n+1
I wonder what the most efficient way to iterate through that NodeList. I can utilize list length etc, but would like to know if there is a more "elegant" way.

The simplest way is a for loop:
for (let i = 0; i < foo.length; i++) {
// Do stuff
}
This is the best solution, as pointed out here it's bad practice to use array methods or convert a NodeList to an array - use a different variable if you need to, but a for loop is all you need to loop over a NodeList. (Thanks Heretic Monkey for pointing this out to me).
If you want to use array methods like forEach, map, etc., it's best convert to an array first - this is incredibly simple with spreading:
[...foo].forEach(e /* Do stuff */);
If you want to specifically use map, use Array.from as the second argument is the callback to be applied to map.
Array.from(foo, e => /* .map callback */);
And in older environments:
Array.prototype.slice.call(foo).forEach(e => /* Do stuff */);
(I know that you can use array methods on a NodeList, but it's easier if you stick to using one data type.)

Although NodeList is not an Array, it is possible to iterate over it with forEach()
See also Why doesn't nodelist have forEach?

Related

[].slice.call() pattern in javascript [duplicate]

This question already has answers here:
Explanation of [].slice.call in javascript?
(9 answers)
Closed 1 year ago.
Bootstrap 5 Javascript examples sometimes show code like:
var collapseElementList = [].slice.call(document.querySelectorAll('.collapse'))
Why isn't this just:
var collapseElementList = document.querySelectorAll('.collapse')
What is [].slice.call() doing exactly? I don't understand why you'd slice on an empty array, and then I have no idea what call is doing there. What would be the problem with the obvious way to do this, the second way above?
querySelectorAll returns a NodeList, which is a collection, but not an array.
[].slice.call turns an array-like collection into an array proper. It will allow you to use array methodss on it, eg:
var collapseElementList = [].slice.call(document.querySelectorAll('.collapse'));
const textsOfCollapsedElements = collapseElementList.map(elm => elm.textContent);
Otherwise, if you don't convert it to an array first, you won't be able to use array methods on it.
It's probably most important for forEach. Newer browsers support NodeList.prototype.forEach, but it hasn't been with us that long. In contrast, Array.prototype.forEach has existed forever. So turning the collection into an array allows for forEach to be called on it, even on obsolete browsers that don't support NodeList.prototype.forEach.
It's an idiom for turning anything array-ish (in this case a DOM NodeList) into a real Array by exploiting the fact that Array::slice can work on anything that's iterable and has a .length property.
The modern idiom is Array.from(...).

Using entries() in a for-of loop, iterating over an HTMLCollection [duplicate]

This question already has answers here:
In JavaScript, what is the best way to convert a NodeList to an array?
(12 answers)
Fastest way to convert JavaScript NodeList to Array?
(15 answers)
Closed 2 years ago.
I'm aware that in a for-of loop, there is the ability to utilise the Array.entries() method. This works great normally, as outlined - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries.
However, if I try to do something like:
for (const [i, foo] of document.getElementsByClassName('foo').entries())
{
console.log(`i = ${i}, foo = ${foo}`);
}
I get told that:
Uncaught TypeError: document.getElementsByClassName.entries is not a function or its return value is not iterable
I know I can just use a good old regular for loop... But:
Why doesn't it work?
Am I misunderstanding something?
Can I get it to work the way I want it to (other than using a regular for loop)?
My best guess is that an HTMLCollection isn't a standard array, and so doesn't have a numeric index as such...
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'));
getElementsByClassName doesn't give an array, but NodeList
for (const [i, foo] of [].entries.call(document.getElementsByClassName('foo')))
{
console.log(`i = ${i}, foo = `, foo);
}

JavaScript - How to add a function treat as inbuilt prototype function [duplicate]

This question already has answers here:
How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop
(4 answers)
Closed 4 years ago.
As I read from the internet I found that I can extend the functionally of inbuild class using prototype. I added some custom functions in Array class like getting the last element from split() function like let file = split("/").last()
Here code how I added the custom function to Array class
Array.prototype.last = function () {
return this[this.length - 1];
}
When any array iterating it add custom function names also with the iteration
let fields = ["test","xyz"];
for(let index in fields) {
}
Above loop iterate for the "last method" also.
As I added 3 functions to Array class named "last", "removeLastElement", "toJson" to extend by using prototype.
Above array iterate 5 times with my custom Array method.
How can I avoid my custom function from an iterating array?
here I debug the array field I found that custom methods for Array class are shown as dark-colored where other inbuilt functions like light colored
You should use a
for...of
loop for array ,instead of using
"For...in "
"For...in" loops are meant to be used to iterate over objects.
You can check the links below for more clarity on this topic:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

js: why use Array.prototype.map.call instead of map()? [duplicate]

This question already has answers here:
why Array.prototype.map.call instead of Array.map.call
(5 answers)
Closed 6 years ago.
This is an exract from casperjs JS :
function getPrices() {
var price = document.querySelectorAll('#InvoiceDetailGrid tbody');
return Array.prototype.map.call(price, function(elem) {
return elem.textContent;
});
}
Why use Array.prototype.map.call: wouldn't be possible to use simply:
price.map() ?
Because price is an array-like(NodeList) object, not an array.
With Array.prototype.map, you are taking a method of Array and use the array-like object as object for mapping.
Why use Array.prototype.map.call: wouldn't be possible to use simply:
price.map() ?
Because you want to call the function you pass in the call as a second argument with a specified this. The this in your case is the first argument: price, The price is an array like object (it has a length property), but it hasn't the methods that arrays have. So the only way you could call the map function on an array like object is this.
If price would have been an array, you could call the map as usually:
price.map(function(item){//...});
The fact that it's not an array routes you to this solution.
Because document.querySelectorAll returns an array-like object which does not have .map method. It doesn't return actual array.

JavaScript For-each/For-in loop changing element types [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
JavaScript “For …in” with Arrays
I'm trying to use the for-in syntax to loop through an array of numbers. Problem is, those numbers are getting converted to strings.
for(var element in [0]) {
document.write(typeof(element)); // outputs "string"
}
Is this standard behavior? I can think of a bunch of ways to work around it, but I'm really just looking for an explaination, to expand my understanding of JavaScript.
I think you misunderstand what JavaScript for...in does. It does not iterate over the array elements. It iterates over object properties. Objects in JavaScript are kind of like dictionaries or hashes in other languages, but keyed by strings. Arrays in particular are implemented as objects which have properties that are integers from 0 to N-1 - however, since all property names are strings, so are the indices, deep down.
Now let's take a bit different example than [0], since here index coincides with the value. Let's discuss [2] instead.
Thus, [2] is, if we ignore the stuff we inherit from Array, pretty much the same as { "0": 2 }.
for..in will iterate over property names, which will pick up the "0", not the 2.
Now, how to iterate over Arrays then, you ask? The usual way is:
var arrayLen = array.length;
for (var i = 0; i < arrayLen; i++) {
var el = array[i];
// ...
}
This is a repeat of Why is using "for...in" with array iteration a bad idea?
The for-in statement enumerates the properties of an object. In your case element is the name of the property and that is always a string.

Categories