I'm trying to loop through childNodes like this:
var children = element.childNodes;
children.forEach(function(item){
console.log(item);
});
However, it output Uncaught TypeError: undefined is not a function due to forEach function. I also try to use children instead of childNodes but nothing changed.
Does anybody know what's going on?
The variable children is a NodeList instance and NodeLists are not true Array and therefore they do not inherit the forEach method.
Also some browsers actually support it nodeList.forEach
ES5
You can use slice from Array to convert the NodeList into a proper Array.
var array = Array.prototype.slice.call(children);
You could also simply use call to invoke forEach and pass it the NodeList as context.
[].forEach.call(children, function(child) {});
ES6
You can use the from method to convert your NodeList into an Array.
var array = Array.from(children);
Or you can also use the spread syntax ... like so
let array = [ ...children ];
A hack that can be used is NodeList.prototype.forEach = Array.prototype.forEach and you can then use forEach with any NodeList without having to convert them each time.
NodeList.prototype.forEach = Array.prototype.forEach
var children = element.childNodes;
children.forEach(function(item){
console.log(item);
});
See A comprehensive dive into NodeLists, Arrays, converting NodeLists and understanding the DOM for a good explanation and other ways to do it.
I'm very late to the party, but since element.lastChild.nextSibling === null, the following seems like the most straightforward option to me:
for(var child=element.firstChild; child!==null; child=child.nextSibling) {
console.log(child);
}
Here is how you can do it with for-in loop.
var children = element.childNodes;
for(var child in children){
console.log(children[child]);
}
const results = Array.from(myNodeList.values()).map(parser_item);
NodeList is not Array
but NodeList.values() return a Array Iterator, so can convert it to Array.
Couldn't resist to add another method, using childElementCount. It returns the number of child element nodes from a given parent, so you can loop over it.
for(var i=0, len = parent.childElementCount ; i < len; ++i){
... do something with parent.children[i]
}
Try with for loop. It gives error in forEach because it is a collection of nodes nodelist.
Or this should convert node-list to array
function toArray(obj) {
var array = [];
for (var i = 0; i < obj.length; i++) {
array[i] = obj[i];
}
return array;
}
Or you can use this
var array = Array.prototype.slice.call(obj);
Here is a functional ES6 way of iterating over a NodeList. This method uses the Array's forEach like so:
Array.prototype.forEach.call(element.childNodes, f)
Where f is the iterator function that receives a child nodes as it's first parameter and the index as the second.
If you need to iterate over NodeLists more than once you could create a small functional utility method out of this:
const forEach = f => x => Array.prototype.forEach.call(x, f);
// For example, to log all child nodes
forEach((item) => { console.log(item); })(element.childNodes)
// The functional forEach is handy as you can easily created curried functions
const logChildren = forEach((childNode) => { console.log(childNode); })
logChildren(elementA.childNodes)
logChildren(elementB.childNodes)
(You can do the same trick for map() and other Array functions.)
Try this [reverse order traversal]:
var childs = document.getElementById('parent').childNodes;
var len = childs.length;
if(len --) do {
console.log('node: ', childs[len]);
} while(len --);
OR [in order traversal]
var childs = document.getElementById('parent').childNodes;
var len = childs.length, i = -1;
if(++i < len) do {
console.log('node: ', childs[i]);
} while(++i < len);
If you do a lot of this sort of thing then it might be worth defining the function for yourself.
if (typeof NodeList.prototype.forEach == "undefined"){
NodeList.prototype.forEach = function (cb){
for (var i=0; i < this.length; i++) {
var node = this[i];
cb( node, i );
}
};
}
for (var i = 0; i < e.childNodes.length; i++) {
var id = e.childNodes[i].id;
}
Related
The NodeList don't have a indexOf(element) method? So, how can I get the element index?
You can use Array.prototype.indexOf.call() like this
let nodes = document.getElementsByTagName('*');
Array.prototype.indexOf.call(nodes, document.body);
The NodeList object is an Array-like object. So it's possible to "convert" it into an Array using Array.prototype.slice.call()
var arr = Array.prototype.slice.call(yourNodeListObject); // Now it's an Array.
arr.indexOf(element); // The index of your element :)
On browsers that support ES6 you can also do this with Array.from()
const arr = Array.from(yourNodeListObject);
or using the spread operator ...
const arr = [...yourNodeListObject];
By iterating over the elements, and checking if it matches.
Generic code that finds the index of the element within it's parents childNodes collection.
function index(el) {
var children = el.parentNode.childNodes,
i = 0;
for (; i < children.length; i++) {
if (children[i] == el) {
return i;
}
}
return -1;
}
Usage:
// should return 4
var idx = index(document.body.childNodes[4]);
EDIT: I can't delete an accepted answer, but #kennebec's answer below is much better, which I'll quote verbatim:
You can use Array.prototype.indexOf.call() like this
let nodes = document.getElementsByTagName('*');
Array.prototype.indexOf.call(nodes, document.body);
Just add one line in your script:
NodeList.prototype.indexOf = Array.prototype.indexOf; // for IE11
Then use indexOf as usual:
var index = NodeList.indexOf(NodeElement);
Let us say you have the following line:
const list=document.querySelectAll('.some_class .someother_class');
list will be a nodelist.
So we can convert the nodelist to an array and create a new array of indexes as follows:
const indexArr= [...list].map( element => return [...list].indexOf(element) );
indexArr contains all the indexes of elements in the original list.
I've sorted following ways to search for an object in array.
This question has been asked like countless times but I want to know which of the best from following ways. If there's another I'd like to know that too.
Using $.grep()
function is_in_array(arr,element){
var result = $.grep(arr, function(e){ return e.id == element; });
return result.length;
}
Above function returns length of array.
0 when element not present
1 when element present
length > 1 if more elements with same value present
using lookup object
var lookup = {};
for (var i = 0, len = array.length; i < len; i++) {
lookup[array[i].id] = array[i];
}
This way I don't need to traverse entire array each time. I'd just check for lookup[id] instead.
for loop in traditional way
function in_array(array, id) {
for(var i=0;i<array.length;i++) {
if(array[i].id === id) {
return true;
}
}
return false;
}
to check if element exists, i'd call in_array(arr,element).
Which approach is best ? Question seriously sounds duplicate and it is but I just want to make sure which is best from these three only.
Update
Array will contain objects like --
var arr = [];
var nameObj = {};
nameObj.label = "somename";
nameObj.id = 123;
arr.push(nameObj);
.
.
.
You could also use a combination of JSON (for comparison) and the Array.filter method:
var findvalue = JSON.stringify([somevalue]),
,found = [array].filter(
function(a){return JSON.stringify(a) === findvalue;}).length
;
// found > 0 means: findvalue found
A jsfiddle example
More about Array.filter and a shim for older browsers #MDN
You could use the built-in map() method instead of a loop:
var lookup=array.map(function(e){return e.id;});
(Not supported in IE 8)
Is it possible to generate a chain of selectors and methods inside of a loop?
For example, I have an array of elements:
array[0] = '.type1value1, .type1value2, .type1value3';
array[1] = '.type2value1, .type2value2, .type2value3';
array[2] = '.type3value1, .type3value2, .type3value3';
I somehow need to build a chain of methods using the array elements as selectors (by looping or any other possible means!) so that I would end up with the following:-
$('.type1value1, .type1value2, .type1value3').filter('.type2value1, .type2.value2, .type2value3').filter('.type3value1, .type3value2, .type3value3');
If I understand you correctly, you don't even need to do a loop:
var firstSelector = array.shift(); //returns first item in the array and removes it from the original array
var filterSelector = array.join(',');
$(firstSelector).filter(filterSelector);
Why can't you just do something like:
var $test = $(array[0]);
for (var i = 1; i < array.length; i++) {
$test = $test.filter(array[i]);
}
Looking at your example, the value of each array element is exactly the value you want to pass as the selector parameter to .filter()
use the following function to pass it an array of selectors...
function getSet(arrSet){
var elements = $(arrSet[0]);
for (var i = 1; i < arrSet.length; i++) {
elements = $(elements).filter(arrSet[i]);
}
}
The NodeList don't have a indexOf(element) method? So, how can I get the element index?
You can use Array.prototype.indexOf.call() like this
let nodes = document.getElementsByTagName('*');
Array.prototype.indexOf.call(nodes, document.body);
The NodeList object is an Array-like object. So it's possible to "convert" it into an Array using Array.prototype.slice.call()
var arr = Array.prototype.slice.call(yourNodeListObject); // Now it's an Array.
arr.indexOf(element); // The index of your element :)
On browsers that support ES6 you can also do this with Array.from()
const arr = Array.from(yourNodeListObject);
or using the spread operator ...
const arr = [...yourNodeListObject];
By iterating over the elements, and checking if it matches.
Generic code that finds the index of the element within it's parents childNodes collection.
function index(el) {
var children = el.parentNode.childNodes,
i = 0;
for (; i < children.length; i++) {
if (children[i] == el) {
return i;
}
}
return -1;
}
Usage:
// should return 4
var idx = index(document.body.childNodes[4]);
EDIT: I can't delete an accepted answer, but #kennebec's answer below is much better, which I'll quote verbatim:
You can use Array.prototype.indexOf.call() like this
let nodes = document.getElementsByTagName('*');
Array.prototype.indexOf.call(nodes, document.body);
Just add one line in your script:
NodeList.prototype.indexOf = Array.prototype.indexOf; // for IE11
Then use indexOf as usual:
var index = NodeList.indexOf(NodeElement);
Let us say you have the following line:
const list=document.querySelectAll('.some_class .someother_class');
list will be a nodelist.
So we can convert the nodelist to an array and create a new array of indexes as follows:
const indexArr= [...list].map( element => return [...list].indexOf(element) );
indexArr contains all the indexes of elements in the original list.
I have two arrays of objects. I would like iterate over each of these two arrays and add a property ("IsPriced"). Then I would like to combine the two arrays into one array.
How can I do this in JavaScript (with MooTools is fine, but not with jQuery please)?
I really don't know where to begin.
You could combine the two arrays first and then iterate only the combined one:
// assuming that your arrays are called array1 and array2:
var combined = array1.concat(array2);
var n = combined.length;
while(n--) {
combined[n].isPriced = true; // or maybe call a getIsPriced function
}
I used a reverse loop since you only need to add a property to all the elements, you don't really care about the order of iteration.
You can use concat to combine the arrays, and each (from MooTools) to apply a function to each item in the combined array.
var first = [{a: "something"}, {b: "or other"}];
var second = [{d: "string"}, {e: "last object"}];
var combined = first.concat(second);
combined.each(function (item) {
item.IsPriced = 10;
});
each is defined by MooTools, so if you're already using MooTools, you might as well use that. ECMAScript now provides a forEach method that does the same thing, but that might not be available on all browsers. If you'd rather use the standard method, the following definition should add it to browsers that don't already support it (from the MDC article, licensed under the MIT license):
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp*/)
{
var len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
fun.call(thisp, this[i], i, this);
}
};
}
To combine two arrays a1 and a2 you can use
var combined = a1.concat(a2);
This creates a new array combining a1 and a2.
If you want to append the contents of a2 to a1 you can use the method described in this StackOverflow post:
a1.push.apply(a1, a2);
To add a new property to each new element in plain JavaScript it is best to use this approach:
combined.map(function(item){item.newProperty = "someValue";});
This avoids iterating over the array manually. Note however that Array.filter()was introduced in JavaScript 1.6.
var totalArr = new Array();
for (var i = 0; i < arr1.length; i++)
{
arr1[i].isPriced = {};
totalArr.push(arr1[i]);
}
for (var i = 0; i < arr2.length; i++)
{
arr2[i].isPriced = {};
totalArr.push(arr1[i]);
}
for (key in myArray1)
myArray1[key].isPrice = 123;
for (key in myArray2)
myArray2[key].isPrice = 123;
result = myArray1.concat(myArray2);