Using foreach to loop through a class element [duplicate] - javascript

This question already has answers here:
How to loop through all the elements returned from getElementsByTagName [duplicate]
(10 answers)
Closed 5 years ago.
I m storing all the elements from a getElementsByClassName to a variable, I would imagine I can foreach loop this variable to get every single ids from it. But its not working.
var el = document.getElementsByClassName("machine_btn_over_layer");
el.forEach(test);
var test = function() {
console.log("test");
}
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
What am I doing wrong? Im getting error saying function is not working

You need to make two changes. First document.getElementsByClassName is a HTMLCollection so array method will not work on this. So to use the array method you can convert it to array using spread operator(...)
Secondly a function declared var test = function() {} is never hoisted. So when el.forEach is called it don't get the function, hence it will throw undefined is not a function
var el = [...document.getElementsByClassName("machine_btn_over_layer")];
var test = function() {
console.log("test");
}
el.forEach(test);
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>

Use the documentation to help you! document.getElementsByClassName:
var elements = document.getElementsByClassName(names)
elements is a live HTMLCollection of found elements.
The first thing you should notice on the HTMLCollection documentation is that it does not have a method named forEach. But it does have a property named length
HTMLCollection.length
Returns the number of items in the collection.
And a method named item
HTMLCollection.item()
Returns the specific node at the given zero-based index into the list. Returns null if the index is out of range.
So you should be able to do this:
for (var i = 0; i < el.length; i++) test(el.item(i));
Or use the array sugar:
for (var i = 0; i < el.length; i++) test(el[i]);

Not every browser implements Array.prototype methods on HTMLCollection returned by getElementsByClassName method.
Also variable assignments do not hoist. Function declarations do.
var el = document.getElementsByClassName("machine_btn_over_layer");
// manually call Array.prototype.forEach on HTMLCollection
Array.prototype.forEach.call(el, test);
// replace function expression with function declaration to hoist with value
function test() {
console.log("test");
}
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>

Related

iterate through all available getElementsByClassName nodes [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
adding 'click' event listeners in loop [duplicate]
(5 answers)
Closed 7 years ago.
For my project, I am required to get the collection of all the elements which have the same class. So, I am doing it with getElementsByClassName.
var tabs = document.getElementsByClassName("tab");
if (tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
console.log(tab); // which outputs as 'undefined'..
console.log(i); // which outputs the nos of the total nodes available with class 'tab'
}
}
I have to retrieve all those elements(which has class 'tab') and apply it another class (say 'bat')..
My question is, why it consoles out undefined as above?? and how can I be able to iterate through all the required nodes sequentially. Is there any code available which can do this??
EDIT ::
Forget to add it..
When clicked on any tab which has 'tabs' class, it always returns the same result..
var tabs = document.getElementsByClassName("tab");
if (tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
tab.addEventListener("click", function() {
console.log(tab[i]); // always returns undefined
console.log(tab); // always returns the last element in the list
console.log(i); // always returns the length of that list (in my case its 3)
}, false);
}
}
How can I be able to iterate through all the available elements sequentially.
You can change a NodeLiist into an array. Btw, your tab[i] should be tabs[i] I think.
var tabs = document.getElementsByClassName('tab');
var tabArray = Array.prototype.slice.call(tabs);
for(var i = 0; i < tabArray.length; i++) {
console.log(tabArray[i].id);
console.log(i);
tabArray[i].innerHTML = "t"+(i+1);
};
<div id="t1" class="tab">t</div>
<div id="t2" class="tab">t</div>
<div id="t3" class="tab">t</div>
<div id="t4" class="tab">t</div>
<div id="t5" class="tab">t</div>
<div id="t6" class="tab">t</div>
<div id="t7" class="tab">t</div>
<div id="t8" class="tab">t</div>
Be careful with getElementsByClassName(), as said in mozilla docs the return is a array-like object not a array. Another point is that return is a live HTMLCollection so if you want to change the class name through a loop the collection will be live updated on the fly and will cause unexpected behaviors.
I made a example with class change case. Check complete code on link below.
http://codepen.io/renatocolaco/pen/VemMNy
function btnClick(event){
event.preventDefault();
//action goes here
var boxes = document.getElementsByClassName("box");
console.log("fouded elements: " + boxes.length);
var element = boxes.item(0);
while(element){
element.className = "another_box";
element = boxes.item(0);
}
//doesn't work
//var cache = boxes.length;
//for(var i = cache; i < boxes.length; i++){
// console.log(boxes[0]);
// boxes[0].className = "another_box";
// }
//doesn't work
//Array.prototype.forEach.call(boxes, function(element, index, array){
//array[index].className = "another_box";
//});
}
Update:
The second problem, when you are adding event to the tabs, you have a closure problem.
This is because, at the point that the onclick method is invoked (for any of the tabs), the for loop has already completed and the variable i already has a value(the size of your collection).
Wrap your code inside a self executed function like this:
(function (i) {
tab.addEventListener('click', function() { console.log(i); });
})(i);
Have you used JQUERY before? That might be an easier path. This way you can use $(".tab"). You can also use .forEach() instead of a for loop? Might make it more simple
Also, Paul's example seems to be working, can you provide a non working JSfiddle?

For loop only iterates once when trying to remove classes from elements

In Javascript I have a function that should find the elements on the page that have the "connected" class, and when a button is clicked the classes for these elements are cleared. I have written this code:
var prev_connected = document.getElementsByClassName("connected");
if (prev_connected.length > 0) {
for (var j = 0; j < prev_connected.length; j++) {
prev_connected[j].removeAttribute("class");
}
}
However, it only ever deletes the class attribute of the first "connected" element on the page. When I have two "connected" elements, I have confirmed that the "prev_connected" array does hold 2 values, but for some reason the for loop never reaches the 2nd one. Is there something I'm doing wrong? Thanks.
The result of getElementsByClassName is live, meaning that as you remove the class attribute it will also remove that element from the result. Using querySelectorAll is more widely supported and returns a static NodeList.
Also, you can more easily iterate the list using a for...in loop.
I would not recommend making an extra copy of the live list just to make it static, you should use a method that returns a static NodeList instead.
var prev_connected = document.querySelectorAll(".connected");
document.getElementById('demo').onclick = function() {
for(var i in Object.keys(prev_connected)) {
prev_connected[i].removeAttribute("class");
}
}
.connected {
background: rgb(150,200,250);
}
<div class="connected">Hello</div>
<div class="connected">Hello</div>
<div class="connected">Hello</div>
<div class="connected">Hello</div>
<div class="connected">Hello</div>
<button id="demo">Remove the classes!</button>
This is due to prev_connected being a live nodelist. When you update the element with that class it removes it from the nodelist which means the length of the nodelist reduces by one which means j is trying to find element 2 in an nodelist of length 1 which is why it doesn't work after the first iteration.
You can see this happening in the console in this demo.
One way you can fix this is by converting the nodelist to an array:
var prev_connected = [].slice.call(document.getElementsByClassName("connected"));
You should iterate in the opposite direction and use elem[i].classList.remove('name') for removing class name from each element Demo
document.getElementById("button").onclick = function () {
var prev_connected = document.getElementsByClassName("connected");
console.log(prev_connected);
for (var i = prev_connected.length - 1; i >= 0; i--) {
prev_connected[i].classList.remove("connected");
console.log(i, prev_connected[i - 1]);
}
}
There are another answers: https://stackoverflow.com/a/14409442/4365315

Iterating over DOM element

I am iterating DOM elements using a for loop, using 2 syntax and in both I am getting different results.
The JavaScript method 1 is
for (var sortable in document.getElementsByClassName('test')) {
console.log("---- " + sortable)
sortable.setAttribute('class', '');
}
Th output for this gives error
undefined is not a function
for sortable.setAttribute('class', ''); line.
And using javascript 2 method
var elements = document.getElementsByClassName("test");
for (var i=0; i< elements.length;i++) {
elements[i].setAttribute("class", "");
}
I get the appropriate result.
My html code is
<span class="test" id="test1">Test1 </span>
<span class="test" id="test2">Test2 </span>
<span class="test" id="test3">Test3 </span>
I don't know why I don't get DOM elements in var sortable in document.getElementsByClassName('test') as sortable?
If you do a for .. in loop in JavaScript, it is important to know that the value returned by the for loop is actually the key and not the value.
So you could try this:
var testElements = document.getElementsByClassName('test');
for (var key in testElements) {
var sortable = testElements[key];
console.log("---- " + sortable)
sortable.setAttribute('class', '');
}
However, this would fail as there are many properties defined on the value returned by getElementsByClassName, which not all return an element.
You can see them all by doing the following:
for (var key in document.getElementsByClassName('test')) {
console.log(key);
}
This will output something like this:
0
1
2
test1
test2
test3
length
item
namedItem
You can see that not only does the returned value contain the numerical indexes, it also has the ID's as properties and also an item and namedItem property.
Unfortunatety, your for-loop also doesn't work as you are changing the class name and getElementsByClassName returns a "live" collection, which means that once you change the class, it is removed from the collection.
You can work around this with a simple loop:
var elements = document.getElementsByClassName("test");
while (elements.length > 0) {
elements[0].setAttribute("class", "");
}
This solution was taken from: Javascript For Loop execution on dom element
getElementsByClassName returns an array of all the elements.
For-in only iterates over objects, not arrays.

Selecting all values inside an array

From what I understand, document.getElementsByClassName returns something like an array object.
How can I modify all returned objects without using jQuery?
Let's say I want to change border-color of all divs that have "class1" applied to them?
<div id="container">
<div class="class1">Hello</div>
<div class="class2">World</div>
<div class="class1">Hello</div>
<div class="class2">World</div>
</div>
Even jQuery will loop over the returned objects. Hiding the for (or while, each or map) loop is just syntax sugar but it's still a loop underneath it all:
var class1 = document.getElementsByClassName('class1');
for (var i = 0; i < class1.length; i++) {
class1[i].style.borderColor = 'green';
}
In one go === in one line?
[].forEach.call(document.getElementsByClassName("class1"), function(c) { c.style.borderColor = "red"; });

GetElementByID - Multiple IDs

doStuff(document.getElementById("myCircle1" "myCircle2" "myCircle3" "myCircle4"));
This doesn't work, so do I need a comma or semi-colon to make this work?
document.getElementById() only supports one name at a time and only returns a single node not an array of nodes. You have several different options:
You could implement your own function that takes multiple ids and returns multiple elements.
You could use document.querySelectorAll() that allows you to specify multiple ids in a CSS selector string .
You could put a common class names on all those nodes and use document.getElementsByClassName() with a single class name.
Examples of each option:
doStuff(document.querySelectorAll("#myCircle1, #myCircle2, #myCircle3, #myCircle4"));
or:
// put a common class on each object
doStuff(document.getElementsByClassName("circles"));
or:
function getElementsById(ids) {
var idList = ids.split(" ");
var results = [], item;
for (var i = 0; i < idList.length; i++) {
item = document.getElementById(idList[i]);
if (item) {
results.push(item);
}
}
return(results);
}
doStuff(getElementsById("myCircle1 myCircle2 myCircle3 myCircle4"));
This will not work, getElementById will query only one element by time.
You can use document.querySelectorAll("#myCircle1, #myCircle2") for querying more then one element.
ES6 or newer
With the new version of the JavaScript, you can also convert the results into an array to easily transverse it.
Example:
const elementsList = document.querySelectorAll("#myCircle1, #myCircle2");
const elementsArray = [...elementsList];
// Now you can use cool array prototypes
elementsArray.forEach(element => {
console.log(element);
});
How to query a list of IDs in ES6
Another easy way if you have an array of IDs is to use the language to build your query, example:
const ids = ['myCircle1', 'myCircle2', 'myCircle3'];
const elements = document.querySelectorAll(ids.map(id => `#${id}`).join(', '));
No, it won't work.
document.getElementById() method accepts only one argument.
However, you may always set classes to the elements and use getElementsByClassName() instead. Another option for modern browsers is to use querySelectorAll() method:
document.querySelectorAll("#myCircle1, #myCircle2, #myCircle3, #myCircle4");
I suggest using ES5 array methods:
["myCircle1","myCircle2","myCircle3","myCircle4"] // Array of IDs
.map(document.getElementById, document) // Array of elements
.forEach(doStuff);
Then doStuff will be called once for each element, and will receive 3 arguments: the element, the index of the element inside the array of elements, and the array of elements.
getElementByID is exactly that - get an element by id.
Maybe you want to give those elements a circle class and getElementsByClassName
document.getElementById() only takes one argument. You can give them a class name and use getElementsByClassName() .
Dunno if something like this works in js, in PHP and Python which i use quite often it is possible.
Maybe just use for loop like:
function doStuff(){
for(i=1; i<=4; i++){
var i = document.getElementById("myCiricle"+i);
}
}
Vulgo has the right idea on this thread. I believe his solution is the easiest of the bunch, although his answer could have been a little more in-depth. Here is something that worked for me. I have provided an example.
<h1 id="hello1">Hello World</h1>
<h2 id="hello2">Random</h2>
<button id="click">Click To Hide</button>
<script>
document.getElementById('click').addEventListener('click', function(){
doStuff();
});
function doStuff() {
for(var i=1; i<=2; i++){
var el = document.getElementById("hello" + i);
el.style.display = 'none';
}
}
</script>
Obviously just change the integers in the for loop to account for however many elements you are targeting, which in this example was 2.
The best way to do it, is to define a function, and pass it a parameter of the ID's name that you want to grab from the DOM, then every time you want to grab an ID and store it inside an array, then you can call the function
<p id="testing">Demo test!</p>
function grabbingId(element){
var storeId = document.getElementById(element);
return storeId;
}
grabbingId("testing").syle.color = "red";
You can use something like this whit array and for loop.
<p id='fisrt'>??????</p>
<p id='second'>??????</p>
<p id='third'>??????</p>
<p id='forth'>??????</p>
<p id='fifth'>??????</p>
<button id="change" onclick="changeColor()">color red</button>
<script>
var ids = ['fisrt','second','third','forth','fifth'];
function changeColor() {
for (var i = 0; i < ids.length; i++) {
document.getElementById(ids[i]).style.color='red';
}
}
</script>
For me worked flawles something like this
doStuff(
document.getElementById("myCircle1") ,
document.getElementById("myCircle2") ,
document.getElementById("myCircle3") ,
document.getElementById("myCircle4")
);
Use jQuery or similar to get access to the collection of elements in only one sentence. Of course, you need to put something like this in your html's "head" section:
<script type='text/javascript' src='url/to/my/jquery.1.xx.yy.js' ...>
So here is the magic:
.- First of all let's supose that you have some divs with IDs as you wrote, i.e.,
...some html...
<div id='MyCircle1'>some_inner_html_tags</div>
...more html...
<div id='MyCircle2'>more_html_tags_here</div>
...blabla...
<div id='MyCircleN'>more_and_more_tags_again</div>
...zzz...
.- With this 'spell' jQuery will return a collection of objects representing all div elements with IDs containing the entire string "myCircle" anywhere:
$("div[id*='myCircle']")
This is all! Note that you get rid of details like the numeric suffix, that you can manipulate all the divs in a single sentence, animate them... Voilá!
$("div[id*='myCircle']").addClass("myCircleDivClass").hide().fadeIn(1000);
Prove this in your browser's script console (press F12) right now!
As stated by jfriend00,
document.getElementById() only supports one name at a time and only returns a single node not an array of nodes.
However, here's some example code I created which you can give one or a comma separated list of id's. It will give you one or many elements in an array. If there are any errors, it will return an array with an Error as the only entry.
function safelyGetElementsByIds(ids){
if(typeof ids !== 'string') return new Error('ids must be a comma seperated string of ids or a single id string');
ids = ids.split(",");
let elements = [];
for(let i=0, len = ids.length; i<len; i++){
const currId = ids[i];
const currElement = (document.getElementById(currId) || new Error(currId + ' is not an HTML Element'));
if(currElement instanceof Error) return [currElement];
elements.push(currElement);
};
return elements;
}
safelyGetElementsByIds('realId1'); //returns [<HTML Element>]
safelyGetElementsByIds('fakeId1'); //returns [Error : fakeId1 is not an HTML Element]
safelyGetElementsByIds('realId1', 'realId2', 'realId3'); //returns [<HTML Element>,<HTML Element>,<HTML Element>]
safelyGetElementsByIds('realId1', 'realId2', 'fakeId3'); //returns [Error : fakeId3 is not an HTML Element]
If, like me, you want to create an or-like construction, where either of the elements is available on the page, you could use querySelector. querySelector tries locating the first id in the list, and if it can't be found continues to the next until it finds an element.
The difference with querySelectorAll is that it only finds a single element, so looping is not necessary.
document.querySelector('#myCircle1, #myCircle2, #myCircle3, #myCircle4');
here is the solution
if (
document.getElementById('73536573').value != '' &&
document.getElementById('1081743273').value != '' &&
document.getElementById('357118391').value != '' &&
document.getElementById('1238321094').value != '' &&
document.getElementById('1118122010').value != ''
) {
code
}
You can do it with document.getElementByID Here is how.
function dostuff (var here) {
if(add statment here) {
document.getElementById('First ID'));
document.getElementById('Second ID'));
}
}
There you go! xD

Categories

Resources